mod utils;
use clap::Parser;
use owo_colors::OwoColorize;
use resext::*;
use std::fs::OpenOptions;
use std::io::{Error, ErrorKind as EK};
use std::path::Path;
use std::process::exit;
use std::sync::LazyLock;
use utils::*;
pub(crate) static ARGS: LazyLock<FioxArgs> = LazyLock::new(FioxArgs::parse);
#[inline]
fn run() -> CtxResult<(), Error> {
let args = &*ARGS;
match &args.cmd {
Commands::Convert {
input,
output,
append,
parse_numbers,
input_delimiter,
output_delimiter,
} => {
throw_err_if!(
!Path::new(&input).exists(),
|| format!(
"{} {} {} {}",
"FATAL:".red().bold(),
"Input file:",
input.to_str().unwrap_or("input_file").on_bright_red(),
"doesn't exist"
),
1
);
let output_file = OpenOptions::new()
.create(true)
.write(true)
.append(*append)
.open(output)
.context("Failed to open output file")?;
let o_d: char;
let now = std::time::Instant::now();
let output_ext;
if let Some(ch) = output_delimiter {
output_ext = std::borrow::Cow::Borrowed("csv");
o_d = *ch;
} else {
output_ext = output
.extension()
.ok_or_else(|| Error::new(EK::InvalidFilename, "Output file has no extension"))
.context("Failed to get output file's extension")?
.to_string_lossy();
o_d = ',';
}
if let Some(ch) = input_delimiter {
let data = csv_decoder::csv_decoder(csv_reader::csv_reader(input, *ch))
.context("Deserialization failed")?;
match_output(data, output_file, &output_ext, *parse_numbers, o_d)?;
} else {
let input_ext: &str = &input
.extension()
.ok_or_else(|| Error::new(EK::InvalidFilename, "Input file has no extension"))
.context("Failed to get input file's extension")?
.to_string_lossy();
match input_ext {
"json" => {
let data = json_decoder::json_decoder(json_reader::json_reader(input))
.context("Deserialization failed")?;
match_output(data, output_file, &output_ext, *parse_numbers, o_d)?;
}
"toml" => {
let data = toml_decoder::toml_decoder(toml_reader::toml_reader(input))
.context("Deserialization failed")?;
match_output(data, output_file, &output_ext, *parse_numbers, o_d)?;
}
"csv" => {
let data = csv_decoder::csv_decoder(csv_reader::csv_reader(input, ','))
.context("Deserialization failed")?;
match_output(data, output_file, &output_ext, *parse_numbers, o_d)?;
}
"ndjson" => {
let data =
ndjson_decoder::ndjson_decoder(ndjson_reader::ndjson_reader(input))
.context("Deserialization failed")?;
match_output(data, output_file, &output_ext, *parse_numbers, o_d)?;
}
_ => log_invalid_ext(input_ext, false)?,
};
}
flush_logger(&format!("Finished in: {:?}", now.elapsed().bright_green()))?;
Ok(())
}
Commands::Validate { input, delimiter } => {
throw_err_if!(
!Path::new(&input).exists(),
|| format!(
"{} {} {} {}",
"FATAL:".red().bold(),
"Input file:",
input.to_str().unwrap_or("input_file").on_bright_red(),
"doesn't exist"
),
1
);
let i_d: char;
let temp_ext;
if let Some(ch) = delimiter {
temp_ext = std::borrow::Cow::Borrowed("csv");
i_d = *ch;
} else {
temp_ext = input
.extension()
.ok_or_else(|| Error::new(EK::InvalidFilename, "Output file has no extension"))
.context("Failed to get output file's extension")?
.to_string_lossy();
i_d = ',';
}
let input_ext: &str = &temp_ext;
let res = match input_ext {
"json" => json_validator::validate_json(input),
"toml" => toml_validator::validate_toml(input),
"csv" => csv_validator::validate_csv(input, i_d),
"ndjson" => ndjson_validator::validate_ndjson(input),
_ => log_invalid_ext(input_ext, false),
};
match res {
Ok(_) => {
let msg = format!("Input file: {} is valid", input.display().bright_green());
flush_logger(&msg)?;
Ok(())
}
Err(e) => {
flush_logger(&e.red().bold().to_string())?;
exit(1);
}
}
}
}
}
fn main() {
match run() {
Ok(_) => {}
Err(e) => {
eprintln!("{} {}", "FATAL:".red().bold(), e);
exit(1);
}
}
}
#[inline]
fn match_output(
data: WriterStreams<impl Iterator<Item = CtxResult<DataTypes, Error>>>,
output_file: std::fs::File,
output_ext: &str,
parse_numbers: bool,
o_d: char,
) -> CtxResult<(), Error> {
match output_ext {
"json" => write_json::write_json(data, output_file, parse_numbers)
.context("Serialization failed")?,
"toml" => toml_writer::toml_writer(data, output_file, parse_numbers)
.context("Serialization failed")?,
"csv" => csv_writer::csv_writer(data, output_file, o_d).context("Serialization failed")?,
"ndjson" => ndjson_writer::ndjson_writer(data, output_file, parse_numbers)
.context("Serialization failed")?,
_ => log_invalid_ext(output_ext, true)?,
};
Ok(())
}
#[inline]
fn log_invalid_ext(input_ext: &str, is_output: bool) -> CtxResult<(), Error> {
let s = if is_output { "Out" } else { "In" };
let repo_link = "https://github.com/Tahaa-Dev/fiux";
Err(Error::new(
EK::InvalidFilename,
format!(
"{}put extension: [{}] is not supported currently\nOpen an issue at: {}",
s,
input_ext.red().bold(),
repo_link.bright_blue().italic(),
),
))?
}