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 output04 / 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;
}
}