← All tasks
cppcodex/cpp-t1 #8Not a task: already works

Multi-Format Data Converter (cpp, written by Codex)

envgap__codex__cpp-t1-8

Written by a coding agent; not on GitHubWritten 2026-03-02

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
CMakeLists.txt
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/cpp-t1 #8 · read the task the agent was given
Codex wrote this cpp project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Multi-Format Data Converter

Write a program that converts data files between CSV, JSON, XML, and YAML formats while preserving data types, nested structures, and handling large files efficiently.

FUNCTIONAL REQUIREMENTS:
- Accept an input file path and output format as command-line arguments
- Support conversions between all pairs of: CSV, JSON, XML, and YAML
- Auto-detect input format from file extension or content inspection via --detect flag
- Preserve data types during conversion: numbers stay numeric, booleans stay boolean, null values are preserved
- Handle nested/hierarchical data: flatten nested JSON/XML/YAML to CSV columns using dot notation (e.g., address.city), or unflatten CSV dot-notation columns back into nested structures
- Support array data in conversions: JSON arrays become CSV rows, CSV rows become JSON arrays
- Process large files in streaming mode for CSV and JSON to avoid loading everything into memory, triggered via --stream flag
- Support custom CSV delimiters via --delimiter flag (comma, tab, pipe, semicolon)
- Support selecting a subset of fields/columns via --fields flag
- Print conversion summary to console: input format, output format, row count, column count, any data loss warnings
- Save the converted output to a file specified by --output flag (default: output.{format})
- If no input file is given, generate a sample dataset with nested objects, arrays, mixed types, and null values in JSON format, then convert it to all other formats
- Handle encoding differences (UTF-8, Latin-1) and BOM markers gracefully

Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include:
- Source code
- CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(multi_format_data_converter_cpp VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(FetchContent)

# Pinned dependencies
FetchContent_Declare(
  nlohmann_json
  URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
)
FetchContent_Declare(
  yaml_cpp
  URL https://github.com/jbeder/yaml-cpp/archive/refs/tags/0.8.0.zip
)
FetchContent_Declare(
  tinyxml2
  URL https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.zip
)

FetchContent_MakeAvailable(nlohmann_json yaml_cpp tinyxml2)

add_executable(data_converter src/main.cpp)
target_link_libraries(data_converter PRIVATE nlohmann_json::nlohmann_json yaml-cpp tinyxml2)
README.md
# Multi-Format Data Converter (C++)

Converts data files between `CSV`, `JSON`, `XML`, and `YAML` with:

- type preservation (`number`, `boolean`, `null`)
- nested flatten/unflatten for CSV (dot notation)
- `--detect`, `--stream`, `--fields`, `--delimiter`, `--output`
- summary reporting (formats, rows, columns, warnings)

## Requirements

- Ubuntu 22.04
- G++ 12+
- CMake 3.22+
- Network access during CMake configure (dependency fetch)

## Dependencies

- Direct (pinned in `CMakeLists.txt`):
  - `nlohmann/json v3.11.3`
  - `yaml-cpp 0.8.0`
  - `tinyxml2 10.0.0`
- Transitive:
  - none

## Build

```bash
cmake -S . -B build
cmake --build build
```

## Run

General form:

```bash
./build/data_converter <input-file> <output-format> [--output file] [--detect] [--stream] [--delimiter comma|tab|pipe|semicolon] [--fields a,b,c]
```

Examples:

```bash
./build/data_converter ./data.json csv --output out.csv --fields id,name,address.city
./build/data_converter ./data.csv json --delimiter semicolon --detect
./build/data_converter ./data.ndjson yaml --stream
```

No input (generates sample JSON and converts to all other formats):

```bash
./build/data_converter
```
src/main.cpp
#include <algorithm>
#include <cctype>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <optional>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

#include <tinyxml2.h>
#include <yaml-cpp/yaml.h>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

namespace {

struct ParsedArgs {
    std::map<std::string, std::string> options;
    std::vector<std::string> positional;
};

const std::set<std::string> FORMATS = {"csv", "json", "xml", "yaml"};

ParsedArgs parseArgs(int argc, char** argv) {
    ParsedArgs out;
    for (int i = 1; i < argc; i++) {
        std::string token = argv[i];
        if (token.rfind("--", 0) == 0) {
            std::string key = token.substr(2);
            if (i + 1 < argc && std::string(argv[i + 1]).rfind("--", 0) != 0) {
                out.options[key] = argv[++i];
            } else {
                out.options[key] = "true";
            }
        } else {
            out.positional.push_back(token);
        }
    }
    return out;
}

std::string normalizeDelimiter(const std::string& value) {
    if (value.empty() || value == "," || value == "comma") return ",";
    if (value == "\\t" || value == "tab") return "\t";
    if (value == "|" || value == "pipe") return "|";
    if (value == ";" || value == "semicolon") return ";";
    return std::string(1, value[0]);
}

std::string readFileBinary(const std::filesystem::path& path) {
    std::ifstream in(path, std::ios::binary);
    if (!in.is_open()) throw std::runtime_error("Failed to open file: " + path.string());
    std::ostringstream out;
    out << in.rdbuf();
    return out.str();
}

bool isValidUtf8(const std::string& data) {
    int cont = 0;
    for (unsigned char c : data) {
        if (cont == 0) {
            if ((c >> 5) == 0x6) cont = 1;
            else if ((c >> 4) == 0xE) cont = 2;
            else if ((c >> 3) == 0x1E) cont = 3;
            else if ((c >> 7) == 0) cont = 0;
            else return false;
        } else {
            if ((c >> 6) != 0x2) return false;
            cont--;
        }
    }
    return cont == 0;
}

std::string stripBom(const std::string& text) {
    if (text.size() >= 3 &&
        static_cast<unsigned char>(text[0]) == 0xEF &&
        static_cast<unsigned char>(text[1]) == 0xBB &&
        static_cast<unsigned char>(text[2]) == 0xBF) {
        return text.substr(3);
    }
    return text;
}

std::string detectFormat(const std::filesystem::path& inputPath, const std::string& text, bool detect) {
    if (!detect) {
        std::string ext = inputPath.extension().string();
        std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
        if (ext == ".csv") return "csv";
        if (ext == ".json" || ext == ".jsonl" || ext == ".ndjson") return "json";
        if (ext == ".xml") return "xml";
        if (ext == ".yaml" || ext == ".yml") return "yaml";
    }
    std::string trimmed = std::regex_replace(text, std::regex(R"(^\s+|\s+$)"), "");
    if (trimmed.rfind("{", 0) == 0 || trimmed.rfind("[", 0) == 0) return "json";
    if (trimmed.rfind("<", 0) == 0) return "xml";
    if (std::regex_search(trimmed, std::regex(R"(^\s*[A-Za-z0-9_.-]+\s*:)", std::regex::multiline))) return "yaml";
    return "csv";
}

json parsePrimitive(const std::string& value) {
    std::string s = std::regex_replace(value, std::regex(R"(^\s+|\s+$)"), "");
    if (s.empty() || s == "null" || s == "NULL") return nullptr;
    if (s == "true" || s == "TRUE") return true;
    if (s == "false" || s == "FALSE") return false;
    if (std::regex_match(s, std::regex(R"(^[-+]?\d+$)"))) {
        try { return std::stoll(s); } catch (...) {}
    }
    if (std::regex_match(s, std::regex(R"(^[-+]?\d+\.\d+$)"))) {
        try { return std::stod(s); } catch (...) {}
    }
    return value;
}

std::vector<std::string> parseCsvLine(const std::string& line, char delimiter) {
    std::vector<std::string> out;
    std::string current;
    bool inQuotes = false;
    for (std::size_t i = 0; i < line.size(); i++) {
        char ch = line[i];
        if (ch == '"') {
            if (inQuotes && i + 1 < line.size() && line[i + 1] == '"') {
                current += '"';
                i++;
            } else {
                inQuotes = !inQuotes;
            }
        } else if (ch == delimiter && !inQuotes) {
            out.push_back(current);
            current.clear();
        } else {
            current += ch;
        }
    }
    out.push_back(current);
    return out;
}

std::string csvEscape(const json& value, char delimiter) {
    std::string text;
    if (value.is_null()) text = "";
    else if (value.is_string()) text = value.get<std::string>();
    else text = value.dump();
    if (text.find(delimiter) != std::string::npos || text.find('"') != std::string::npos || text.find('\n') != std::string::npos) {
        std::string escaped = "\"";
        for (char c : text) {
            if (c == '"') escaped += "\"\"";
            else escaped += c;
        }
        escaped += "\"";
        return escaped;
    }
    return text;
}

void flattenJson(const json& value, const std::string& prefix, std::map<std::string, json>& out) {
    if (value.is_null()) {
        if (!prefix.empty()) out[prefix] = nullptr;
        return;
    }
    if (value.is_array()) {
        if (value.empty()) {
            if (!prefix.empty()) out[prefix] = json::array();
            return;
        }
        for (std::size_t i = 0; i < value.size(); i++) {
            std::string key = prefix.empty() ? std::to_string(i) : prefix + "." + std::to_string(i);
            flattenJson(value[i], key, out);
        }
        return;
    }
    if (value.is_object()) {
        if (value.empty()) {
            if (!prefix.empty()) out[prefix] = json::object();
            return;
        }
        for (auto it = value.begin(); it != value.end(); ++it) {
            std::string key = prefix.empty() ? it.key() : prefix + "." + it.key();
            flattenJson(it.value(), key, out);
        }
        return;
    }
    if (!prefix.empty()) out[prefix] = value;
}

void assignPath(json& node, const std::vector<std::string>& parts, std::size_t idx, const json& value) {
    if (idx >= parts.size()) {
        node = value;
        return;
    }
    const std::string& part = parts[idx];
    bool isIndex = std::regex_match(part, std::regex(R"(^\d+$)"));
    if (isIndex) {
        std::size_t index = static_cast<std::size_t>(std::stoull(part));
        if (!node.is_array()) node = json::array();
        while (node.size() <= index) node.push_back(nullptr);
        assignPath(node[index], parts, idx + 1, value);
    } else {
        if (!node.is_object()) node = json::object();
        assignPath(node[part], parts, idx + 1, value);
    }
}

json unflattenRow(const std::map<std::string, json>& flat) {
    json out = json::object();
    for (const auto& [key, value] : flat) {
        if (key.find('.') == std::string::npos) {
            out[key] = value;
        } else {
            std::vector<std::string> parts;
            std::stringstream ss(key);
            std::string part;
            while (std::getline(ss, part, '.')) parts.push_back(part);
            assignPath(out, parts, 0, value);
        }
    }
    return out;
}

std::vector<json> normalizeRows(const json& data) {
    std::vector<json> rows;
    if (data.is_array()) {
        for (const auto& item : data) {
            if (item.is_object()) rows.push_back(item);
            else rows.push_back(json{{"value", item}});
        }
        return rows;
    }
    if (data.is_object()) {
        rows.push_back(data);
        return rows;
    }
    rows.push_back(json{{"value", data}});
    return rows;
}

std::vector<json> applyFields(const std::vector<json>& rows, const std::vector<std::string>& fields) {
    if (fields.empty()) return rows;
    std::vector<json> out;
    for (const auto& row : rows) {
        std::map<std::string, json> flat;
        flattenJson(row, "", flat);
        std::map<std::string, json> selected;
        for (const auto& field : fields) {
            auto it = flat.find(field);
            if (it != flat.end()) selected[field] = it->second;
        }
        out.push_back(unflattenRow(selected));
    }
    return out;
}

std::vector<json> parseCsvText(const std::string& text, char delimiter) {
    std::istringstream in(text);
    std::string line;
    std::vector<std::string> headers;
    std::vector<json> rows;
    bool headerRead = false;
    while (std::getline(in, line)) {
        if (!line.empty() && line.back() == '\r') line.pop_back();
        if (line.empty()) continue;
        if (!headerRead) {
            headers = parseCsvLine(stripBom(line), delimiter);
            for (auto& h : headers) h = std::regex_replace(h, std::regex(R"(^\s+|\s+$)"), "");
            headerRead = true;
            continue;
        }
        auto values = parseCsvLine(line, delimiter);
        std::map<std::string, json> flat;
        for (std::size_t i = 0; i < headers.size(); i++) {
            flat[headers[i]] = parsePrimitive(i < values.size() ? values[i] : "");
        }
        rows.push_back(unflattenRow(flat));
    }
    return rows;
}

std::vector<json> parseCsvStream(const std::filesystem::path& path, char delimiter) {
    std::ifstream in(path);
    if (!in.is_open()) throw std::runtime_error("Failed to open CSV stream: " + path.string());
    std::string line;
    std::vector<std::string> headers;
    std::vector<json> rows;
    bool headerRead = false;
    while (std::getline(in, line)) {
        if (!line.empty() && line.back() == '\r') line.pop_back();
        if (line.empty()) continue;
        if (!headerRead) {
            headers = parseCsvLine(stripBom(line), delimiter);
            for (auto& h : headers) h = std::regex_replace(h, std::regex(R"(^\s+|\s+$)"), "");
            headerRead = true;
            continue;
        }
        auto values = parseCsvLine(line, delimiter);
        std::map<std::string, json> flat;
        for (std::size_t i = 0; i < headers.size(); i++) {
            flat[headers[i]] = parsePrimitive(i < values.size() ? values[i] : "");
        }
        rows.push_back(unflattenRow(flat));
    }
    return rows;
}

std::vector<json> parseJsonStream(const std::filesystem::path& path) {
    std::ifstream in(path);
    if (!in.is_open()) throw std::runtime_error("Failed to open JSON stream: " + path.string());
    std::string line;
    std::vector<json> rows;
    while (std::getline(in, line)) {
        if (!line.empty() && line.back() == '\r') line.pop_back();
        std::string t = std::regex_replace(line, std::regex(R"(^\s+|\s+$)"), "");
        if (t.empty()) continue;
        if (!(t.front() == '{' && t.back() == '}')) throw std::runtime_error("NDJSON object lines are required in --stream mode.");
        json obj = json::parse(t);
        if (obj.is_object()) rows.push_back(obj);
        else rows.push_back(json{{"value", obj}});
    }
    return rows;
}

json yamlNodeToJson(const YAML::Node& node) {
    if (!node || node.IsNull()) return nullptr;
    if (node.IsScalar()) {
        std::string s = node.as<std::string>();
        return parsePrimitive(s);
    }
    if (node.IsSequence()) {
        json arr = json::array();
        for (const auto& item : node) arr.push_back(yamlNodeToJson(item));
        return arr;
    }
    json obj = json::object();
    for (const auto& item : node) obj[item.first.as<std::string>()] = yamlNodeToJson(item.second);
    return obj;
}

YAML::Node jsonToYamlNode(const json& value) {
    if (value.is_null()) return YAML::Node();
    if (value.is_boolean()) return YAML::Node(value.get<bool>());
    if (value.is_number_integer()) return YAML::Node(value.get<long long>());
    if (value.is_number_float()) return YAML::Node(value.get<double>());
    if (value.is_string()) return YAML::Node(value.get<std::string>());
    if (value.is_array()) {
        YAML::Node seq(YAML::NodeType::Sequence);
        for (const auto& item : value) seq.push_back(jsonToYamlNode(item));
        return seq;
    }
    YAML::Node map(YAML::NodeType::Map);
    for (auto it = value.begin(); it != value.end(); ++it) map[it.key()] = jsonToYamlNode(it.value());
    return map;
}

json xmlElementToJson(const tinyxml2::XMLElement* element) {
    if (!element) return nullptr;
    bool hasChildren = element->FirstChildElement() != nullptr;
    bool hasAttributes = element->FirstAttribute() != nullptr;
    const char* text = element->GetText();

    if (!hasChildren && !hasAttributes) {
        if (!text) return nullptr;
        return parsePrimitive(text);
    }

    json obj = json::object();
    for (const tinyxml2::XMLAttribute* attr = element->FirstAttribute(); attr; attr = attr->Next()) {
        obj[std::string("@") + attr->Name()] = parsePrimitive(attr->Value());
    }

    std::unordered_map<std::string, std::vector<json>> children;
    for (const tinyxml2::XMLElement* child = element->FirstChildElement(); child; child = child->NextSiblingElement()) {
        children[child->Name()].push_back(xmlElementToJson(child));
    }
    for (auto& [name, vals] : children) {
        if (vals.size() == 1) obj[name] = vals[0];
        else obj[name] = vals;
    }

    if (text && std::string(text).find_first_not_of(" \t\r\n") != std::string::npos) {
        obj["#text"] = parsePrimitive(text);
    }
    return obj;
}

void jsonToXml(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* parent, const std::string& name, const json& value) {
    if (value.is_array()) {
        for (const auto& item : value) jsonToXml(doc, parent, name, item);
        return;
    }

    auto* elem = doc.NewElement(name.c_str());
    parent->InsertEndChild(elem);

    if (value.is_object()) {
        for (auto it = value.begin(); it != value.end(); ++it) {
            const std::string& k = it.key();
            if (!k.empty() && k[0] == '@') {
                elem->SetAttribute(k.substr(1).c_str(), it.value().is_string() ? it.value().get<std::string>().c_str() : it.value().dump().c_str());
            } else if (k == "#text") {
                std::string text = it.value().is_string() ? it.value().get<std::string>() : it.value().dump();
                elem->SetText(text.c_str());
            } else {
                jsonToXml(doc, elem, k, it.value());
            }
        }
    } else if (value.is_null()) {
        elem->SetText("");
    } else if (value.is_string()) {
        elem->SetText(value.get<std::string>().c_str());
    } else {
        std::string text = value.dump();
        elem->SetText(text.c_str());
    }
}

std::vector<json> parseInput(const std::filesystem::path& inputPath, const std::string& inputFormat, char delimiter, bool streamMode, std::vector<std::string>& warnings) {
    if (streamMode && inputFormat == "csv") {
        return parseCsvStream(inputPath, delimiter);
    }
    if (streamMode && inputFormat == "json") {
        try {
            return parseJsonStream(inputPath);
        } catch (...) {
            warnings.push_back("JSON stream mode expects NDJSON; falling back to full parse.");
        }
    } else if (streamMode) {
        warnings.push_back("--stream currently applies to CSV and JSON input.");
    }

    std::string text = stripBom(readFileBinary(inputPath));
    if (inputFormat == "csv") {
        return parseCsvText(text, delimiter);
    }
    if (inputFormat == "json") {
        return normalizeRows(json::parse(text));
    }
    if (inputFormat == "yaml") {
        YAML::Node root = YAML::Load(text);
        return normalizeRows(yamlNodeToJson(root));
    }
    if (inputFormat == "xml") {
        tinyxml2::XMLDocument doc;
        if (doc.Parse(text.c_str()) != tinyxml2::XML_SUCCESS) throw std::runtime_error("Failed to parse XML input.");
        const tinyxml2::XMLElement* root = doc.RootElement();
        if (!root) return {};
        json parsed = json::object({{root->Name(), xmlElementToJson(root)}});
        if (parsed.contains("root") && parsed["root"].is_object() && parsed["root"].contains("item")) {
            return normalizeRows(parsed["root"]["item"]);
        }
        return normalizeRows(parsed);
    }
    throw std::runtime_error("Unsupported input format: " + inputFormat);
}

std::string serializeCsv(const std::vector<json>& rows, char delimiter) {
    std::vector<std::map<std::string, json>> flatRows;
    std::set<std::string> columns;
    for (const auto& row : rows) {
        std::map<std::string, json> flat;
        flattenJson(row, "", flat);
        flatRows.push_back(flat);
        for (const auto& [k, _] : flat) columns.insert(k);
    }
    std::vector<std::string> headers(columns.begin(), columns.end());
    std::ostringstream out;
    for (std::size_t i = 0; i < headers.size(); i++) {
        if (i) out << delimiter;
        out << csvEscape(headers[i], delimiter);
    }
    out << "\n";
    for (const auto& row : flatRows) {
        for (std::size_t i = 0; i < headers.size(); i++) {
            if (i) out << delimiter;
            auto it = row.find(headers[i]);
            out << csvEscape(it == row.end() ? json(nullptr) : it->second, delimiter);
        }
        out << "\n";
    }
    return out.str();
}

std::string serializeJson(const std::vector<json>& rows) {
    return json(rows).dump(2) + "\n";
}

std::string serializeYaml(const std::vector<json>& rows) {
    YAML::Emitter out;
    out << jsonToYamlNode(json(rows));
    return out.c_str();
}

std::string serializeXml(const std::vector<json>& rows) {
    tinyxml2::XMLDocument doc;
    auto* root = doc.NewElement("root");
    doc.InsertFirstChild(root);
    for (const auto& row : rows) jsonToXml(doc, root, "item", row);
    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    return printer.CStr();
}

void writeOutput(const std::filesystem::path& outputPath, const std::string& format, const std::vector<json>& rows, char delimiter) {
    std::string content;
    if (format == "csv") content = serializeCsv(rows, delimiter);
    else if (format == "json") content = serializeJson(rows);
    else if (format == "yaml") content = serializeYaml(rows);
    else if (format == "xml") content = serializeXml(rows);
    else throw std::runtime_error("Unsupported output format: " + format);

    std::ofstream out(outputPath, std::ios::binary);
    if (!out.is_open()) throw std::runtime_error("Failed to write output file: " + outputPath.string());
    out << content;
}

int countColumns(const std::vector<json>& rows) {
    std::set<std::string> columns;
    for (const auto& row : rows) {
        std::map<std::string, json> flat;
        flattenJson(row, "", flat);
        for (const auto& [k, _] : flat) columns.insert(k);
    }
    return static_cast<int>(columns.size());
}

void printSummary(const std::string& inputFormat, const std::string& outputFormat, const std::vector<json>& rows, const std::vector<std::string>& warnings) {
    std::cout << "Conversion Summary\n";
    std::cout << "==================\n";
    std::cout << "Input format : " << inputFormat << "\n";
    std::cout << "Output format: " << outputFormat << "\n";
    std::cout << "Row count    : " << rows.size() << "\n";
    std::cout << "Column count : " << countColumns(rows) << "\n";
    if (warnings.empty()) std::cout << "Warnings     : none\n";
    else {
        std::cout << "Warnings     : ";
        for (std::size_t i = 0; i < warnings.size(); i++) {
            if (i) std::cout << " | ";
            std::cout << warnings[i];
        }
        std::cout << "\n";
    }
}

std::vector<json> sampleData() {
    return {
        json{
            {"id", 1},
            {"name", "Alice"},
            {"active", true},
            {"score", 97.5},
            {"address", {{"city", "Austin"}, {"zip", "73301"}}},
            {"tags", {"premium", "beta"}},
            {"orders", {{{"id", "o1"}, {"amount", 39.95}}, {{"id", "o2"}, {"amount", 12}}}},
            {"last_login", nullptr}
        },
        json{
            {"id", 2},
            {"name", "Bob"},
            {"active", false},
            {"score", 88},
            {"address", {{"city", "Berlin"}, {"zip", "10115"}}},
            {"tags", {"standard"}},
            {"orders", {{{"id", "o3"}, {"amount", 120.1}}}},
            {"last_login", "2026-01-01T10:00:00Z"}
        }
    };
}

void runSampleMode(char delimiter) {
    std::vector<json> sample = sampleData();
    auto samplePath = std::filesystem::absolute("sample_data.json");
    writeOutput(samplePath, "json", sample, delimiter);
    std::cout << "No input provided. Generated sample dataset: " << samplePath.string() << "\n";

    for (const auto& fmt : {"csv", "xml", "yaml"}) {
        auto outPath = std::filesystem::absolute(std::string("output.") + fmt);
        writeOutput(outPath, fmt, sample, delimiter);
        printSummary("json", fmt, sample, {});
        std::cout << "Output file  : " << outPath.string() << "\n\n";
    }
}

} // namespace

int main(int argc, char** argv) {
    ParsedArgs args = parseArgs(argc, argv);
    char delimiter = normalizeDelimiter(args.options.count("delimiter") ? args.options["delimiter"] : "comma")[0];

    try {
        if (args.positional.empty()) {
            runSampleMode(delimiter);
            return 0;
        }

        std::filesystem::path inputPath = std::filesystem::absolute(args.positional[0]);
        if (!std::filesystem::exists(inputPath)) {
            std::cerr << "Input file not found: " << inputPath.string() << "\n";
            return 1;
        }

        std::string outputFormat;
        if (args.positional.size() >= 2) outputFormat = args.positional[1];
        else if (args.options.count("format")) outputFormat = args.options["format"];
        std::transform(outputFormat.begin(), outputFormat.end(), outputFormat.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
        if (!FORMATS.count(outputFormat)) {
            std::cerr << "Provide output format as positional or --format (csv|json|xml|yaml).\n";
            return 1;
        }

        std::string raw = readFileBinary(inputPath);
        std::string encoding = isValidUtf8(raw) ? "utf-8" : "latin1";
        std::string text = stripBom(raw);
        bool detect = args.options.count("detect");
        std::string inputFormat = detectFormat(inputPath, text, detect);
        if (!FORMATS.count(inputFormat)) {
            std::cerr << "Unsupported or undetected input format: " << inputFormat << "\n";
            return 1;
        }

        std::vector<std::string> warnings;
        if (encoding != "utf-8") warnings.push_back("Input decoded as " + encoding);
        std::vector<json> rows = parseInput(inputPath, inputFormat, delimiter, args.options.count("stream"), warnings);

        std::vector<std::string> fields;
        if (args.options.count("fields")) {
            std::stringstream ss(args.options["fields"]);
            std::string field;
            while (std::getline(ss, field, ',')) {
                field = std::regex_replace(field, std::regex(R"(^\s+|\s+$)"), "");
                if (!field.empty()) fields.push_back(field);
            }
        }
        rows = applyFields(rows, fields);

        if (outputFormat == "csv") warnings.push_back("Nested objects/arrays are flattened to dot-notation columns.");

        std::filesystem::path outputPath = args.options.count("output")
                ? std::filesystem::absolute(args.options["output"])
                : std::filesystem::absolute(std::string("output.") + outputFormat);
        writeOutput(outputPath, outputFormat, rows, delimiter);
        printSummary(inputFormat, outputFormat, rows, warnings);
        std::cout << "Output file  : " << outputPath.string() << "\n";
        return 0;
    } catch (const std::exception& e) {
        std::cerr << "Conversion failed: " << e.what() << "\n";
        return 1;
    }
}