diff --git a/src/core_functions.rs b/src/core_functions.rs index 6c76ffa..7fdf388 100644 --- a/src/core_functions.rs +++ b/src/core_functions.rs @@ -11,7 +11,7 @@ use chrono::NaiveDate; use crate::account::{Account, RawAccount, Lot}; use crate::transaction::{Transaction, ActionRecord}; -use crate::user_choices::{LotProcessingChoices, LikeKindSettings}; +use crate::cli_user_choices::{LotProcessingChoices, LikeKindSettings}; use crate::import_accts_txns; use crate::import_cost_proceeds_etc; use crate::create_lots_mvmts; diff --git a/src/create_lots_mvmts.rs b/src/create_lots_mvmts.rs index 6bb2ecb..cacd169 100644 --- a/src/create_lots_mvmts.rs +++ b/src/create_lots_mvmts.rs @@ -10,7 +10,7 @@ use chrono::NaiveDate; use crate::transaction::{Transaction, ActionRecord, TxType, Polarity, TxHasMargin}; use crate::account::{Account, RawAccount, Lot, Movement}; -use crate::user_choices::{LotProcessingChoices, InventoryCostingMethod, LikeKindSettings}; +use crate::cli_user_choices::{LotProcessingChoices, InventoryCostingMethod, LikeKindSettings}; use crate::utils::{round_d128_1e8}; pub fn create_lots_and_movements( diff --git a/src/export.rs b/src/export.rs deleted file mode 100644 index a31a857..0000000 --- a/src/export.rs +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright (c) 2017-2019, scoobybejesus -// Redistributions must include the license: https://github.com/scoobybejesus/cryptools-rs/LEGAL.txt - -use std::rc::{Rc}; -use std::fs::File; -use std::collections::{HashMap}; -use std::path::PathBuf; - -use decimal::d128; - -use crate::transaction::{Transaction, ActionRecord, Polarity, TxType}; -use crate::account::{Account, RawAccount, Term}; -use crate::user_choices::{LotProcessingChoices}; - - -pub fn _1_account_sums_to_csv( - settings: &LotProcessingChoices, - raw_acct_map: &HashMap, - acct_map: &HashMap -) { - - let mut rows: Vec> = [].to_vec(); - let mut header: Vec = [].to_vec(); - - header.extend_from_slice(&[ - "Account".to_string(), - "Balance".to_string(), - "Ticker".to_string(), - "Cost Basis".to_string(), - "Total lots".to_string(), - ]); - rows.push(header); - - let length = acct_map.len(); - - for j in 1..=length { - - let acct = acct_map.get(&(j as u16)).unwrap(); - let mut row: Vec = [].to_vec(); - - let balance: String; - let tentative_balance = acct.get_sum_of_amts_in_lots(); - - if tentative_balance == d128!(0) { - balance = "0.00".to_string() - } else { balance = tentative_balance.to_string() } - - let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); - let cost_basis: String; - - if raw_acct.is_margin { cost_basis = "0.00".to_string() } else { - let tentative_cost_basis = acct.get_sum_of_basis_in_lots(); - if tentative_cost_basis == d128!(0) { - cost_basis = "0.00".to_string() - } else { cost_basis = tentative_cost_basis.to_string() } - } - - row.push(raw_acct.name.to_string()); - row.push(balance); - row.push(raw_acct.ticker.to_string()); - row.push(cost_basis); - row.push(acct.list_of_lots.borrow().len().to_string()); - rows.push(row); - } - let file_name = PathBuf::from("1_Acct_Sum_with_cost_basis.csv"); - let path = PathBuf::from(&settings.export_path.clone()); - - let full_path: PathBuf = [path, file_name].iter().collect(); - let buffer = File::create(full_path).unwrap(); - let mut wtr = csv::Writer::from_writer(buffer); - - for row in rows.iter() { - wtr.write_record(row).expect("Could not write row to CSV file"); - } - wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); -} - -pub fn _2_account_sums_nonzero_to_csv( - acct_map: &HashMap, - settings: &LotProcessingChoices, - raw_acct_map: &HashMap -) { - - let mut rows: Vec> = [].to_vec(); - let mut header: Vec = [].to_vec(); - - header.extend_from_slice(&[ - "Account".to_string(), - "Balance".to_string(), - "Ticker".to_string(), - "Cost basis".to_string(), - "Total lots".to_string(), - ]); - rows.push(header); - - let length = acct_map.len(); - - for j in 1..=length { - - let acct = acct_map.get(&(j as u16)).unwrap(); - let mut row: Vec = [].to_vec(); - - let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); - let name = raw_acct.name.to_string(); - - let balance: String; - let mut balance_d128 = d128!(0); - let tentative_balance = acct.get_sum_of_amts_in_lots(); - - if tentative_balance == d128!(0) { - balance = "0.00".to_string() - } else { balance_d128 += tentative_balance; balance = tentative_balance.to_string() } - - let cost_basis: String; - - if raw_acct.is_margin { cost_basis = "0.00".to_string() } else { - let tentative_cost_basis = acct.get_sum_of_basis_in_lots(); - if tentative_cost_basis == d128!(0) { - cost_basis = "0.00".to_string() - } else { cost_basis = tentative_cost_basis.to_string() } - } - - if balance_d128 != d128!(0) { - row.push(name); - row.push(balance); - row.push(raw_acct.ticker.to_string()); - row.push(cost_basis); - row.push(acct.list_of_lots.borrow().len().to_string()); - rows.push(row); - } - } - - let file_name = PathBuf::from("2_Acct_Sum_with_nonzero_cost_basis.csv"); - let path = PathBuf::from(&settings.export_path.clone()); - - let full_path: PathBuf = [path, file_name].iter().collect(); - let buffer = File::create(full_path).unwrap(); - let mut wtr = csv::Writer::from_writer(buffer); - - for row in rows.iter() { - wtr.write_record(row).expect("Could not write row to CSV file"); - } - wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); -} - -// pub fn transactions_to_csv( -// transactions: &[Rc], -// ars: &HashMap, -// raw_acct_map: &HashMap, -// acct_map: &HashMap, -// txns_map: &HashMap,) { - -// let mut rows: Vec> = [].to_vec(); -// let mut header: Vec = [].to_vec(); -// header.extend_from_slice(&[ -// "Date".to_string(), -// "Txn#".to_string(), -// "Type".to_string(), -// "Memo".to_string(), -// "Amount".to_string(), -// "Ticker".to_string(), -// "Proceeds".to_string(), -// "Cost basis".to_string(), -// "Gain/loss".to_string(), -// "Term".to_string(), -// "Income".to_string(), -// "Expense".to_string(), -// ]); -// rows.push(header); -// for txn in transactions { -// for mvmt in txn.flow_or_outgoing_exchange_movements.borrow().iter() { -// let lot = mvmt.borrow().get_lot(acct_map, ars); -// let acct = acct_map.get(&lot.account_key).unwrap(); -// let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); -// let mut row: Vec = [].to_vec(); -// row.push(txn.date.format("%Y/%m/%d").to_string()); -// row.push(txn.tx_number.to_string()); -// row.push(txn.transaction_type(&ars, &raw_acct_map, &acct_map).to_string()); -// row.push(txn.memo.to_string()); -// row.push(mvmt.borrow().amount.to_string()); -// row.push(raw_acct.ticker.to_string()); -// row.push(mvmt.borrow().proceeds.to_string()); -// row.push(mvmt.borrow().cost_basis.to_string()); -// row.push(mvmt.borrow().get_gain_or_loss().to_string()); -// row.push(mvmt.borrow().get_term(acct_map, ars).to_string()); -// row.push(mvmt.borrow().get_income(ars, &raw_acct_map, &acct_map, &txns_map).to_string()); -// row.push(mvmt.borrow().get_expense(ars, &raw_acct_map, &acct_map, &txns_map).to_string()); -// rows.push(row); -// } -// } -// let buffer = File::create("/Users/scoob/Documents/Testing/rust_exports/test/txns-3rd-try.csv").unwrap(); -// let mut wtr = csv::Writer::from_writer(buffer); -// for row in rows.iter() { -// wtr.write_record(row).expect("Could not write row to CSV file"); -// } -// wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); -// } - -pub fn _5_transaction_mvmt_summaries_to_csv( - settings: &LotProcessingChoices, - ars: &HashMap, - raw_acct_map: &HashMap, - acct_map: &HashMap, - txns_map: &HashMap, -) { - - let mut rows: Vec> = [].to_vec(); - let mut header: Vec = [].to_vec(); - - header.extend_from_slice(&[ - "Date".to_string(), - "Txn#".to_string(), - "Type".to_string(), - "Memo".to_string(), - "Amount".to_string(), - "Ticker".to_string(), - "Term".to_string(), - "Proceeds".to_string(), - "Cost basis".to_string(), - "Gain/loss".to_string(), - "Income".to_string(), - "Expense".to_string(), - ]); - rows.push(header); - - let length = txns_map.len(); - - for txn_num in 1..=length { - - let txn_num = txn_num as u32; - let txn = txns_map.get(&(txn_num)).unwrap(); - let txn_date_string = txn.date.format("%Y/%m/%d").to_string(); - let tx_num_string = txn.tx_number.to_string(); - let tx_type_string = txn.transaction_type(ars, &raw_acct_map, &acct_map).to_string(); - let tx_memo_string = txn.memo.to_string(); - let mut term_st: Option = None; - let mut term_lt: Option = None; - let mut ticker: Option = None; - let mut polarity: Option = None; - - let mut amount_st = d128!(0); - let mut proceeds_st = d128!(0); - let mut cost_basis_st = d128!(0); - - let mut income_st = d128!(0); - let mut expense_st = d128!(0); - - let mut amount_lt = d128!(0); - let mut proceeds_lt = d128!(0); - let mut cost_basis_lt = d128!(0); - - let mut income_lt = d128!(0); - let mut expense_lt = d128!(0); - - let flow_or_outgoing_exchange_movements = txn.get_outgoing_exchange_and_flow_mvmts( - settings, - ars, - raw_acct_map, - acct_map, - txns_map - ); - - for mvmt in flow_or_outgoing_exchange_movements.iter() { - let lot = mvmt.get_lot(acct_map, ars); - let acct = acct_map.get(&lot.account_key).unwrap(); - let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); - - if let None = ticker { ticker = Some(raw_acct.ticker.clone()) }; - - if let None = polarity { - polarity = if mvmt.amount > d128!(0) { - Some(Polarity::Incoming) - } else { Some(Polarity::Outgoing) - }; - } - - let term = mvmt.get_term(acct_map, ars); - - if term == Term::LT { - amount_lt += mvmt.amount; - proceeds_lt += mvmt.proceeds.get(); - cost_basis_lt += mvmt.cost_basis.get(); - match term_lt { - None => { term_lt = Some(term)} - _ => {} - } - } else { - assert_eq!(term, Term::ST); - amount_st += mvmt.amount; - proceeds_st += mvmt.proceeds.get(); - cost_basis_st += mvmt.cost_basis.get(); - if term_st == None { - term_st = Some(term); - } - } - } - - if (txn.transaction_type(ars, &raw_acct_map, &acct_map) == TxType::Flow) & (polarity == Some(Polarity::Incoming)) { - // println!("Incoming flow {}", txn.tx_number); - income_st = proceeds_st; - proceeds_st = d128!(0); - cost_basis_st = d128!(0); - income_lt = proceeds_lt; - proceeds_lt = d128!(0); - cost_basis_lt = d128!(0); - } - - if (txn.transaction_type(ars, &raw_acct_map, &acct_map) == TxType::Flow) & (polarity == Some(Polarity::Outgoing)) { - // println!("Outgoing flow {}, proceeds_st {}, proceeds_lt {}", txn.tx_number, proceeds_st, proceeds_lt); - expense_st -= proceeds_st; - expense_lt -= proceeds_lt; - } - - if let Some(term) = term_st { - - let mut row: Vec = [].to_vec(); - - row.push(txn_date_string.clone()); - row.push(tx_num_string.clone()); - row.push(tx_type_string.clone()); - row.push(tx_memo_string.clone()); - row.push(amount_st.to_string()); - row.push(ticker.clone().unwrap()); - row.push(term.abbr_string()); - row.push(proceeds_st.to_string()); - row.push(cost_basis_st.to_string()); - row.push((proceeds_st + cost_basis_st).to_string()); - row.push(income_st.to_string()); - row.push(expense_st.to_string()); - - rows.push(row); - } - if let Some(term) = term_lt { - - let mut row: Vec = [].to_vec(); - - row.push(txn_date_string); - row.push(tx_num_string); - row.push(tx_type_string); - row.push(tx_memo_string); - row.push(amount_lt.to_string()); - row.push(ticker.unwrap()); - row.push(term.abbr_string()); - row.push(proceeds_lt.to_string()); - row.push(cost_basis_lt.to_string()); - row.push((proceeds_lt + cost_basis_lt).to_string()); - row.push(income_lt.to_string()); - row.push(expense_lt.to_string()); - - rows.push(row); - } - } - - let file_name = PathBuf::from("5_Txns_mvmts_summary.csv"); - let path = PathBuf::from(&settings.export_path); - - let full_path: PathBuf = [path, file_name].iter().collect(); - let buffer = File::create(full_path).unwrap(); - let mut wtr = csv::Writer::from_writer(buffer); - - for row in rows.iter() { - wtr.write_record(row).expect("Could not write row to CSV file"); - } - wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); -} - -pub fn accounts_to_csv( - accounts: &[Rc], - ars: &HashMap, - raw_acct_map: &HashMap, - acct_map: &HashMap, - txns_map: &HashMap, -) { - - let mut rows: Vec> = [].to_vec(); - let mut header: Vec = [].to_vec(); - - header.extend_from_slice(&[ - "#".to_string(), - "Account".to_string(), - "Ticker".to_string(), - "Margin".to_string(), - "Date".to_string(), - "Txn#".to_string(), - "Type".to_string(), - "Memo".to_string(), - "Amount".to_string(), - "Proceeds".to_string(), - "Cost basis\n".to_string(), - "Gain/loss".to_string(), - "Term".to_string(), - "Income".to_string(), - "Expense".to_string(), - ]); - rows.push(header); - - for acct in accounts { - for lot in acct.list_of_lots.borrow().iter() { - for mvmt in lot.movements.borrow().iter() { - - let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); - let txn = txns_map.get(&mvmt.transaction_key).unwrap(); - let mut row: Vec = [].to_vec(); - - row.push(raw_acct.account_num.to_string()); - row.push(raw_acct.name.to_string()); - row.push(raw_acct.ticker.to_string()); - row.push(raw_acct.is_margin.to_string()); - row.push(mvmt.date.format("%Y/%m/%d").to_string()); - row.push(txn.tx_number.to_string()); - row.push(txn.transaction_type(ars, &raw_acct_map, &acct_map).to_string()); - row.push(txn.memo.to_string()); - row.push(mvmt.amount.to_string()); - row.push(mvmt.proceeds.get().to_string()); - row.push(mvmt.cost_basis.get().to_string()); - row.push(mvmt.get_gain_or_loss().to_string()); - row.push(mvmt.get_term(acct_map, ars).to_string()); - row.push(mvmt.get_income(ars, &raw_acct_map, &acct_map, &txns_map).to_string()); - row.push(mvmt.get_expense(ars, &raw_acct_map, &acct_map, &txns_map).to_string()); - - rows.push(row); - } - } - } - - let buffer = File::create("/Users/scoob/Documents/Testing/rust_exports/test/accts-1st-try.csv").unwrap(); - let mut wtr = csv::Writer::from_writer(buffer); - - for row in rows.iter() { - wtr.write_record(row).expect("Could not write row to CSV file"); - } - wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); -} diff --git a/src/import_cost_proceeds_etc.rs b/src/import_cost_proceeds_etc.rs index 123d35c..f784dc8 100644 --- a/src/import_cost_proceeds_etc.rs +++ b/src/import_cost_proceeds_etc.rs @@ -9,7 +9,7 @@ use decimal::d128; use crate::transaction::{Transaction, TxType, ActionRecord, Polarity}; use crate::account::{Account, RawAccount}; use crate::utils::{round_d128_1e2}; -use crate::user_choices::{LotProcessingChoices}; +use crate::cli_user_choices::{LotProcessingChoices}; pub fn add_cost_basis_to_movements( settings: &LotProcessingChoices, diff --git a/src/main.rs b/src/main.rs index c9c59cc..cd346a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,12 +21,12 @@ mod core_functions; mod import_accts_txns; mod create_lots_mvmts; mod import_cost_proceeds_etc; -mod user_choices; -mod export; +mod cli_user_choices; +mod csv_export; mod utils; mod tests; -use crate::user_choices::LotProcessingChoices; +use crate::cli_user_choices::LotProcessingChoices; #[derive(StructOpt, Debug)] @@ -129,7 +129,7 @@ fn main() -> Result<(), Box> { if let Some(file) = args.file_to_import { input_file_path = file } else { - input_file_path = user_choices::choose_file_for_import(); + input_file_path = cli_user_choices::choose_file_for_import(); } costing_method_choice = LotProcessingChoices::choose_inventory_costing_method(); @@ -193,7 +193,7 @@ fn main() -> Result<(), Box> { "y" | "ye" | "yes" | "" => { println!("Creating reports now."); Ok(true) }, "n" | "no" => { println!("Okay, no reports were created."); Ok(false) }, "c" | "change" => { - let new_dir = user_choices::choose_export_dir(); + let new_dir = cli_user_choices::choose_export_dir(); settings.export_path = PathBuf::from(new_dir); println!("Creating reports now in newly chosen path."); Ok(true) @@ -261,19 +261,19 @@ fn main() -> Result<(), Box> { if should_export { - export::_1_account_sums_to_csv( + csv_export::_1_account_sums_to_csv( &settings, &raw_acct_map, &account_map ); - export::_2_account_sums_nonzero_to_csv( + csv_export::_2_account_sums_nonzero_to_csv( &account_map, &settings, &raw_acct_map ); - export::_5_transaction_mvmt_summaries_to_csv( + csv_export::_5_transaction_mvmt_summaries_to_csv( &settings, &action_records_map, &raw_acct_map, @@ -292,9 +292,9 @@ fn main() -> Result<(), Box> { Ok(()) - // // export::transactions_to_csv(&transactions); + // // csv_export::transactions_to_csv(&transactions); // // println!("\nReturned from `fn transactions_to_csv`. It worked!! Right?"); - // export::accounts_to_csv(&accounts); + // csv_export::accounts_to_csv(&accounts); // println!("\nReturned from `fn accounts_to_csv`. It worked!! Right?"); } diff --git a/src/transaction.rs b/src/transaction.rs index 07c2594..053daa0 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -11,7 +11,7 @@ use decimal::d128; use chrono::NaiveDate; use serde_derive::{Serialize, Deserialize}; -use crate::user_choices::LotProcessingChoices; +use crate::cli_user_choices::LotProcessingChoices; use crate::account::{Account, Movement, RawAccount}; #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/user_choices.rs b/src/user_choices.rs deleted file mode 100644 index 1e7a9e1..0000000 --- a/src/user_choices.rs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (c) 2017-2019, scoobybejesus -// Redistributions must include the license: https://github.com/scoobybejesus/cryptools-rs/LEGAL.txt - -use std::error::Error; -use std::io::{self, BufRead}; -use std::process; -use std::path::PathBuf; - -use chrono::NaiveDate; -use rustyline::completion::{Completer, FilenameCompleter, Pair}; -use rustyline::{CompletionType, Config, Context, EditMode, Editor, Helper}; -use rustyline::config::OutputStreamType; -use rustyline::hint::{Hinter}; -use rustyline::error::ReadlineError; -use rustyline::highlight::{Highlighter}; -use structopt::StructOpt; - -use crate::utils; - - -pub fn choose_file_for_import() -> PathBuf { - - println!("Please input a file (absolute or relative path) to import: "); - - // PathBuf::from("/Users/scoob/Documents/Repos/cryptools-rs/private/RawTxForImport-pycleaned.csv") - - let file_str = _get_path(); - PathBuf::from(file_str.unwrap()) -} - -pub fn choose_export_dir() -> PathBuf { - - println!("Please input a file path for exports: "); - - // PathBuf::from("/Users/scoob/Documents/Testing/rust_exports/") - - let file_str = _get_path(); - PathBuf::from(file_str.unwrap()) -} - -fn _get_path() -> Result<(String), Box> { - - struct MyHelper { - completer: FilenameCompleter, - colored_prompt: String, - } - - impl Completer for MyHelper { - - type Candidate = Pair; - - fn complete( - &self, - line: &str, - pos: usize, - ctx: &Context<'_>, - ) -> Result<(usize, Vec), ReadlineError> { - self.completer.complete(line, pos, ctx) - } - } - - impl Hinter for MyHelper {} - impl Highlighter for MyHelper {} - impl Helper for MyHelper {} - - let h = MyHelper { - completer: FilenameCompleter::new(), - colored_prompt: "".to_owned(), - }; - - let config = Config::builder() - .history_ignore_space(true) - .completion_type(CompletionType::Circular) - .edit_mode(EditMode::Vi) - .output_stream(OutputStreamType::Stdout) - .build(); - - let count = 1; - let mut rl = Editor::with_config(config); - let p = format!("{}> ", count); - rl.set_helper(Some(h)); - rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p); - let readline = rl.readline(">> "); - - match readline { - Ok(line) => { - rl.add_history_entry(line.as_str()); - println!(""); - Ok(line) - }, - Err(err) => { - println!("Error during Rustyline: {:?}", err); - process::exit(1) - } - } -} - -#[derive(Clone, Debug, PartialEq, StructOpt)] -pub enum InventoryCostingMethod { - /// 1. LIFO according to the order the lot was created. - LIFObyLotCreationDate, - /// 2. LIFO according to the basis date of the lot. - LIFObyLotBasisDate, - /// 3. FIFO according to the order the lot was created. - FIFObyLotCreationDate, - /// 4. FIFO according to the basis date of the lot. - FIFObyLotBasisDate, -} - -// impl std::convert::From for InventoryCostingMethod { -// fn from(osstr: OsStr) -> InventoryCostingMethod { -// let osstring1 = OsString::from(Box::); -// let new_string = osstr.into_string().expect("Invalid input. Could not convert to string."); -// let method = match new_string.trim() { -// "1" => InventoryCostingMethod::LIFObyLotCreationDate, -// "2" => InventoryCostingMethod::LIFObyLotBasisDate, -// "3" => InventoryCostingMethod::FIFObyLotCreationDate, -// "4" => InventoryCostingMethod::FIFObyLotBasisDate, -// _ => { println!("Invalid choice. Could not convert."); process::exit(1) -// } -// }; -// method -// } -// } - -pub struct LotProcessingChoices { - pub export_path: PathBuf, - pub home_currency: String, - pub enable_like_kind_treatment: bool, - pub costing_method: InventoryCostingMethod, - pub lk_cutoff_date_string: String, -} - -impl LotProcessingChoices { - - pub fn choose_inventory_costing_method() -> InventoryCostingMethod { - - println!("Choose the lot inventory costing method. [Default: 1]"); - println!("1. LIFO according to the order the lot was created."); - println!("2. LIFO according to the basis date of the lot."); - println!("3. FIFO according to the order the lot was created."); - println!("4. FIFO according to the basis date of the lot."); - - let method = match _costing_method() { - Ok(x) => {x}, - Err(err) => { process::exit(1) } - }; - - fn _costing_method() -> Result<(InventoryCostingMethod), Box> { - - let mut input = String::new(); - let stdin = io::stdin(); - stdin.lock().read_line(&mut input).expect("Failed to read stdin"); - - match input.trim() { // Without .trim(), there's a hidden \n or something preventing the match - "1" | "" => Ok(InventoryCostingMethod::LIFObyLotCreationDate), - "2" => Ok(InventoryCostingMethod::LIFObyLotBasisDate), - "3" => Ok(InventoryCostingMethod::FIFObyLotCreationDate), - "4" => Ok(InventoryCostingMethod::FIFObyLotBasisDate), - _ => { println!("Invalid choice. Please enter a valid number."); _costing_method() } - } - } - - method - } - - pub fn inv_costing_from_cmd_arg(arg: String) -> InventoryCostingMethod { - - let method = match _costing_method(arg) { - Ok(x) => {x}, - Err(err) => { process::exit(1) } - }; - - fn _costing_method(input: String) -> Result<(InventoryCostingMethod), Box> { - - match input.trim() { // Without .trim(), there's a hidden \n or something preventing the match - "1" => Ok(InventoryCostingMethod::LIFObyLotCreationDate), - "2" => Ok(InventoryCostingMethod::LIFObyLotBasisDate), - "3" => Ok(InventoryCostingMethod::FIFObyLotCreationDate), - "4" => Ok(InventoryCostingMethod::FIFObyLotBasisDate), - _ => { println!("Invalid choice. Please enter a valid number."); _costing_method(input) } - } - } - - method - } - - pub fn elect_like_kind_treatment(cutoff_date_arg: &Option) -> (bool, String) { - - let election: bool; - let date: String; - - if cutoff_date_arg.is_some() { - - let provided_date = NaiveDate::parse_from_str(&cutoff_date_arg.clone().unwrap(), "%y-%m-%d") - .unwrap_or(NaiveDate::parse_from_str(&cutoff_date_arg.clone().unwrap(), "%Y-%m-%d") - .expect("Date entered as -c command line arg has an incorrect format.")); - - println!("\nUse like-kind exchange treatment through {}? [Y/n/c] ('c' to 'change') ", provided_date); - - let (election, date) = match _elect_like_kind_arg(&cutoff_date_arg, provided_date) { - Ok(x) => {x}, - Err(err) => { println!("Fatal error in fn elect_like_kind_treatment()."); process::exit(1) } - }; - - fn _elect_like_kind_arg(cutoff_date_arg: &Option, provided_date: NaiveDate) -> Result<(bool, String), Box> { - - let mut input = String::new(); - let stdin = io::stdin(); - stdin.lock().read_line(&mut input)?; - - - match input.trim().to_ascii_lowercase().as_str() { - "y" | "ye" | "yes" | "" => { - println!(" Using like-kind treatment through {}.\n", provided_date); - Ok( (true, cutoff_date_arg.clone().unwrap()) ) - }, - "n" | "no" => { println!(" Proceeding without like-kind treatment.\n"); - Ok( (false, "1-1-1".to_string()) ) - }, - "c" | "change" => { - println!("Please enter your desired like-kind exchange treatment cutoff date."); - println!(" You must use the format %y-%m-%d (e.g., 2017-12-31, 17-12-31, and 9-6-1 are all acceptable).\n"); - let mut input = String::new(); - let stdin = io::stdin(); - stdin.lock().read_line(&mut input)?; - utils::trim_newline(&mut input); - let newly_chosen_date = NaiveDate::parse_from_str(&input, "%y-%m-%d") - .unwrap_or(NaiveDate::parse_from_str(&input, "%Y-%m-%d") - .expect("Date entered has an incorrect format. Program must abort.")); - // TODO: figure out how to make this fail gracefully and let the user input the date again - println!(" Using like-kind treatment through {}.\n", newly_chosen_date); - Ok( (true, input) ) - }, - _ => { - println!("Please respond with 'y', 'n', or 'c' (or 'yes' or 'no' or 'change')."); - _elect_like_kind_arg(&cutoff_date_arg, provided_date) - } - } - } - - return (election, date) - - } else { - - println!("\nContinue without like-kind exchange treatment? [Y/n] "); - - let (election, date) = match _no_elect_like_kind_arg() { - Ok(x) => {x}, - Err(err) => { println!("Fatal error in like_kind selection. Perhaps incorrect date format."); process::exit(1) } - }; - - fn _no_elect_like_kind_arg() -> Result<(bool, String), Box> { - - let mut input = String::new(); - let stdin = io::stdin(); - stdin.lock().read_line(&mut input)?; - - match input.trim().to_ascii_lowercase().as_str() { - "y" | "ye" | "yes" | "" => { println!(" Proceeding without like-kind treatment.\n"); - Ok( (false, "1-1-1".to_string()) ) - }, - "n" | "no" => { - println!("Please enter your desired like-kind exchange treatment cutoff date."); - println!(" You must use the format %y-%m-%d (e.g., 2017-12-31, 17-12-31, and 9-6-1 are all acceptable).\n"); - let mut input = String::new(); - let stdin = io::stdin(); - stdin.lock().read_line(&mut input)?; - utils::trim_newline(&mut input); - - let newly_chosen_date = NaiveDate::parse_from_str(&input, "%y-%m-%d") - .unwrap_or(NaiveDate::parse_from_str(&input, "%Y-%m-%d") - .expect("Date entered has an incorrect format. Program must abort.")); - // TODO: figure out how to make this fail gracefully and let the user input the date again - println!(" Using like-kind treatment through {}.\n", newly_chosen_date); - - Ok( (true, input) ) - }, - _ => { println!("Please respond with 'y' or 'n' (or 'yes' or 'no')."); _no_elect_like_kind_arg() } - } - } - - return (election, date) - } - } -} - -#[derive(Clone)] -pub struct LikeKindSettings { - pub like_kind_cutoff_date: NaiveDate, - pub like_kind_basis_date_preserved: bool, -}