use clap::Parser;
use csv_ledger_lib::{ledger::Ledger, LedgerErr};
use std::{
env,
fs::{self, File},
io::BufReader,
path::PathBuf,
process::ExitCode,
};
#[derive(Parser, Debug)]
#[clap(author, version, about)]
struct Args {
path: PathBuf,
#[clap(short = 'o', long = "output")]
output: Option<PathBuf>,
}
impl Args {
fn parse_input() -> Result<Args, clap::Error> {
if cfg!(feature = "test_args") && env::var("CSV_LEDGER_TEST_ARGS").is_ok() {
match env::var("CSV_LEDGER_PATH") {
Ok(p) => Ok(Args {
path: p.into(),
output: env::var("CSV_LEDGER_OUTPUT").ok().map(|s| s.into()),
}),
Err(_) => Err(clap::Error::with_description(
"CSV_LEDGER_PATH environment variable not set.".to_string(),
clap::ErrorKind::MissingRequiredArgument,
)),
}
} else {
Args::try_parse()
}
}
}
fn main() -> ExitCode {
let args = match Args::parse_input() {
Ok(args) => args,
Err(err) => {
eprintln!("{err}");
return ExitCode::FAILURE;
}
};
if let Err(err) = perform_parse_and_output(args.path, args.output) {
eprintln!("{err}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}
#[inline]
pub fn perform_parse_and_output(path: PathBuf, output: Option<PathBuf>) -> Result<(), LedgerErr> {
let file = File::open(path).map_err(LedgerErr::Opening)?;
let mut ledger = Ledger::default();
ledger.consume_csv(BufReader::new(file))?;
if let Some(output_path) = output {
fs::write(output_path, ledger.to_string()).map_err(LedgerErr::Saving)?;
} else {
println!("{}", ledger);
}
Ok(())
}
#[cfg(test)]
mod perform_parse_and_output {
use std::{fs, path::Path};
use tempfile::tempdir;
#[test]
fn ok_stdout() {
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let input = "type, client, tx, amount\ndeposit, 1, 1, 1.0";
fs::write(&path, input).expect("Failed to create temporary file");
let result = super::perform_parse_and_output(path.clone().into(), None);
assert!(result.is_ok());
}
#[test]
fn ok_output_file() {
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let output = dir.path().join("test_output.csv");
let input = "type, client, tx, amount\ndeposit, 1, 1, 1.0";
fs::write(&path, input).expect("Unable to write file");
let result =
super::perform_parse_and_output(path.clone().into(), Some(output.clone().into()));
result.unwrap();
assert!(Path::new(&output).is_file());
}
#[test]
fn err_read_file() {
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("/foo/test.csv");
let result = super::perform_parse_and_output(path.clone().into(), None);
assert!(result.is_err());
}
#[test]
fn err_consume() {
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let input = "";
fs::write(&path, input).expect("Failed to create temporary file");
let result = super::perform_parse_and_output(path.clone().into(), None);
assert!(result.is_err());
}
#[test]
fn err_output_file() {
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let output = dir.path().join("/example/test_output.csv");
let input = "type, client, tx, amount\ndeposit, 1, 1, 1.0";
fs::write(&path, input).expect("Unable to write file");
let result =
super::perform_parse_and_output(path.clone().into(), Some(output.clone().into()));
assert!(result.is_err());
}
}
#[cfg(test)]
mod args {
use super::Args;
use clap::Parser;
#[test]
fn debug() {
let args = Args {
path: "./tests/test.csv".into(),
output: Some("./tests/test_output.csv".into()),
};
assert_eq!(
format!("{:?}", args),
"Args { path: \"./tests/test.csv\", output: Some(\"./tests/test_output.csv\") }"
);
}
#[test]
fn parse_err() {
Args::try_parse_from(["foo.csv"]).unwrap_err();
}
}
#[cfg(all(test, feature = "test_args"))]
mod main {
use crate::main;
use std::{env, fs};
use tempfile::tempdir;
fn reset_args() {
env::remove_var("CSV_LEDGER_TEST_ARGS");
env::remove_var("CSV_LEDGER_OUTPUT");
env::remove_var("CSV_LEDGER_PATH");
}
#[test]
fn ok_stdout() {
reset_args();
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let input = "type, client, tx, amount\ndeposit, 1, 1, 1.0";
fs::write(&path, input).expect("Unable to write file");
env::set_var("CSV_LEDGER_TEST_ARGS", "true");
env::set_var("CSV_LEDGER_PATH", path);
main();
}
#[test]
fn ok_file() {
reset_args();
let dir = tempdir().expect("Failed to create temporary directory");
let path = dir.path().join("test.csv");
let output = dir.path().join("test_output.csv");
let input = "type, client, tx, amount\ndeposit, 1, 1, 1.0";
fs::write(&path, input).expect("Unable to write file");
env::set_var("CSV_LEDGER_TEST_ARGS", "true");
env::set_var("CSV_LEDGER_PATH", path);
env::set_var("CSV_LEDGER_OUTPUT", output);
main();
}
#[test]
fn err_invalid_path() {
reset_args();
let dir = tempdir().expect("Failed to create temporary directory");
env::set_var("CSV_LEDGER_TEST_ARGS", "true");
env::set_var("CSV_LEDGER_PATH", dir.path().join("foo.csv"));
main();
}
#[test]
fn err_missing_path() {
reset_args();
env::set_var("CSV_LEDGER_TEST_ARGS", "true");
main();
}
#[test]
fn err_default_args() {
reset_args();
main();
}
}