#![forbid(unsafe_code)]
mod cli_args;
use std::error::Error;
use std::io;
use std::path::PathBuf;
use log::error;
use tackler_core::export::{EquityExporter, EquitySettings, Export, IdentityExporter};
use tackler_core::kernel::Settings;
use tackler_core::parser;
use tackler_core::parser::GitInputSelector;
use tackler_core::report::{
BalanceGroupReporter, BalanceGroupSettings, BalanceReporter, BalanceSettings, RegisterReporter,
RegisterSettings, Report,
};
use clap::Parser;
use tackler_api::filters::FilterDefinition;
use tackler_api::txn_ts;
use tackler_core::kernel::hash::Hash;
use tackler_core::kernel::settings::Audit;
use time_tz::timezones;
#[cfg(not(target_env = "msvc"))]
use tikv_jemallocator::Jemalloc;
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
fn run() -> Result<i32, Box<dyn Error>> {
let cli = cli_args::Cli::parse();
let hash = if let Some(audit) = cli.audit_mode {
if audit {
Some(Hash::default())
} else {
None
}
} else {
None
};
let cfg = Settings {
basedir: PathBuf::new().into_boxed_path(),
accounts: cli.accounts,
audit: Audit { hash },
};
let result = if cli.input_filename.is_some()
|| (cli.input_fs_dir.is_some() && cli.input_fs_ext.is_some())
{
if let Some(filename) = cli.input_filename {
parser::paths_to_txns(&[filename], &cfg)
} else {
let paths = tackler_rs::get_paths_by_ext(
cli.input_fs_dir.unwrap().as_path(),
&cli.input_fs_ext.unwrap(),
)?;
parser::paths_to_txns(&paths, &cfg)
}
} else if cli.input_git_repo.is_some()
&& cli.input_git_dir.is_some()
&& cli.input_git_ref.is_some()
{
parser::git_to_txns(
cli.input_git_repo.unwrap().as_path(),
cli.input_git_dir.as_deref().unwrap(),
"txn",
GitInputSelector::Reference(cli.input_git_ref.unwrap()),
&cfg,
)
} else {
return Err("No input".into());
};
let txn_filt = match cli.api_filter_def {
Some(filt_str) => {
if FilterDefinition::is_armored(&filt_str) {
Some(FilterDefinition::from_armor(&filt_str)?)
} else {
Some(FilterDefinition::from_json_str(&filt_str)?)
}
}
None => None,
};
match result {
Ok(txn_data) => {
let txn_set = match txn_filt {
Some(tf) => txn_data.filter(&tf)?,
None => txn_data.get_all()?,
};
if let Some(reports) = cli.reports {
let mut w: Box<dyn io::Write> = Box::new(io::stdout());
if let Some(metadata) = &txn_set.metadata() {
writeln!(&mut w, "{}", metadata.text())?;
}
for r in reports {
match r.as_str() {
"balance" => {
let bal_reporter = BalanceReporter {
report_settings: BalanceSettings {
title: Some("BALANCE".to_string()),
ras: &cfg.accounts,
},
};
bal_reporter.write_txt_report(&cfg, &mut w, &txn_set)?;
}
"balance-group" => {
let report_tz = cli.report_tz.clone().unwrap();
let group_by = cli.group_by.clone().unwrap();
let bal_group_reporter = BalanceGroupReporter {
report_settings: BalanceGroupSettings {
title: Some("BALANCE GROUP".to_string()), ras: &cfg.accounts,
group_by: txn_ts::GroupBy::from(&group_by)?,
report_tz: timezones::get_by_name(&report_tz)
.ok_or(format!("Can't recognise tz [{report_tz}]"))?,
},
};
bal_group_reporter.write_txt_report(&cfg, &mut w, &txn_set)?;
}
"register" => {
let reg_reporter = RegisterReporter {
report_settings: RegisterSettings {
title: Some("REGISTER".to_string()),
ras: &cfg.accounts,
},
};
reg_reporter.write_txt_report(&cfg, &mut w, &txn_set)?;
}
_ => {
return Err("Internal Logic error with reports cli args".into());
}
}
}
}
if let Some(exports) = cli.exports {
let mut w: Box<dyn io::Write> = Box::new(io::stdout());
for e in exports {
match e.as_str() {
"equity" => {
let eqa = match &cli.equity_account_name {
Some(e) => e,
_ => {
return Err("Internal Logic error with exports cli args (equity account name)".into());
}
};
let eq_exporter = EquityExporter {
export_settings: EquitySettings {
eqa: Some(eqa.clone()),
ras: &cfg.accounts,
},
};
eq_exporter.write_export(&cfg, &mut w, &txn_set)?;
}
"identity" => {
let eq_exporter = IdentityExporter {};
eq_exporter.write_export(&cfg, &mut w, &txn_set)?;
}
_ => {
return Err("Internal Logic error with exports cli args".into());
}
}
}
}
Ok(0)
}
Err(err) => {
let msg = format!("Error with transaction input: {err}");
error!("{}", msg);
Err(msg.into())
}
}
}
fn main() {
match run() {
Ok(_) => std::process::exit(0),
Err(err) => {
let msg = format!("Tackler error: {err}");
error!("{msg}");
eprintln!("{msg}");
std::process::exit(1)
}
}
}