use std::error::Error; use std::fs::File; use chrono::{NaiveDateTime}; use crate::transaction::{Transaction, Input, Output}; pub mod account; pub mod transaction; use std::env; use std::borrow::Borrow; use std::collections::HashMap; fn parse_bisq_transactions(account:&String) -> Result, Box>{ // Build the CSV reader and iterate over each record. let file = File::open(String::from(account) + "/bisq/transactions.csv")?; let mut rdr = csv::Reader::from_reader(file); let mut trade_transactions: Vec= vec!(); for result in rdr.records() { let record = result?; let details = &record[1]; let date = &record[0]; let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?; let amount:f64 = record[4].parse().unwrap(); if details.contains("Maker and tx fee") { let mut transaction = Transaction::new(date_time, format!("Maker Fee")); transaction.add_input(Input{ source: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Expenses:MakerFee"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } if details.contains("Taker and tx fee") { let mut transaction = Transaction::new(date_time, format!("Taker Fee")); transaction.add_input(Input{ source: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Expenses:TakerFee"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } if details.contains("Multisig deposit") { let mut transaction = Transaction::new(date_time, format!("Multisig Deposit")); transaction.add_input(Input{ source: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Expenses:TradingDeposit"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } if details.contains("Multisig payout") { let mut transaction = Transaction::new(date_time, format!("Multisig Payout")); transaction.add_input(Input{ source: String::from("Expenses:TradingDeposit"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } if details.contains("Withdrawn from wallet") { let mut transaction = Transaction::new(date_time, format!("Withdrawal from Bisq Trading Wallet")); transaction.add_input(Input{ source: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Assets:General"), amount: ((amount.abs() * 100.0).round() / 100.0), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Expenses:TxFee"), amount: ((amount.abs() - ((amount.abs() * 100.0).round() / 100.0)) * 100000000.0).round() / 100000000.0, value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } if details.contains("Received funds") { let mut transaction = Transaction::new(date_time, format!("Deposit into Bisq Trading Wallet")); transaction.add_input(Input{ source: String::from("Assets:General"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_output(Output{ destination: String::from("Assets:Trading"), amount: amount.abs(), value: String::from("BTC") }); transaction.add_comment(record[2].to_string()); trade_transactions.push(transaction); } } return Ok(trade_transactions); } fn parse_bisq_trades(account:&String) -> Result, Box>{ // Build the CSV reader and iterate over each record. let file = File::open(String::from(account) + "/bisq/tradeHistory.csv")?; let mut rdr = csv::Reader::from_reader(file); let mut trade_transactions: Vec= vec!(); for result in rdr.records() { let record = result?; let id = &record[0]; let date = &record[1]; let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?; let bought = &record[4]; let sold = &record[5]; let mut transaction = Transaction::new(date_time, format!("Bisq Trade {}", id)); transaction.add_input(Input{ source: String::from("Assets:Trading"), amount: sold.replace(" ZEC","").parse().unwrap(), value: String::from("ZEC") }); transaction.add_output(Output{ destination: String::from("Expenses:TradingDeposit"), amount: bought.parse().unwrap(), value: String::from("BTC") }); trade_transactions.push(transaction); } return Ok(trade_transactions); } fn parse_donations(account:&String) -> Result, Box>{ // Build the CSV reader and iterate over each record. let file = File::open(String::from(account) + "/donations.csv")?; let mut rdr = csv::Reader::from_reader(file); let mut trade_transactions: Vec= vec!(); for result in rdr.records() { // The iterator yields Result, so we check the // error here.. let record = result?; let date = &record[0]; let date_time = NaiveDateTime::parse_from_str(date,"%b %e, %Y %I:%M:%S %p")?; let details = &record[1]; let amount = &record[2]; let denomination = &record[3]; let account = &record[4]; let mut transaction = Transaction::new(date_time, format!("{}", details)); transaction.add_input(Input{ source: String::from("Income:Donations"), amount: amount.parse().unwrap(), value: String::from(denomination), }); transaction.add_output(Output{ destination: String::from(account), amount: amount.parse().unwrap(), value: String::from(denomination), }); trade_transactions.push(transaction); } return Ok(trade_transactions); } fn price_db(historical_price_file:&String) -> Result, Box> { // Build the CSV reader and iterate over each record. let file = File::open(String::from(historical_price_file))?; let mut rdr = csv::Reader::from_reader(file); let mut pricedb: HashMap = HashMap::new(); for result in rdr.records() { let record = result?; let date = &record[0]; let date_time = NaiveDateTime::parse_from_str(date,"%Y-%m-%d %H:%M:%S UTC")?; //println!("[{}] {}", date_time, &record[1]); let value :f64 = record[1].parse().unwrap(); pricedb.insert(date_time, value); } return Ok(pricedb) } fn main() { let args: Vec = env::args().collect(); let mut ledger = account::Account::new(); let zectocad = price_db(String::from("./zec-cad.csv").borrow()).unwrap(); for (k,v) in &zectocad { println!("P {} ZEC {} $", k, v); } let btctocad = price_db(String::from("./btc-cad.csv").borrow()).unwrap(); for (k,v) in &btctocad { println!("P {} BTC {} $", k, v); } let donations = parse_donations(args[1].borrow()).unwrap(); for transaction in donations.iter() { ledger.add_transaction(transaction.clone()); } let trade_transactions = parse_bisq_trades(args[1].borrow()).unwrap(); for transaction in trade_transactions.iter() { ledger.add_transaction(transaction.clone()); } let bisq_transactions = parse_bisq_transactions(args[1].borrow()).unwrap(); for transaction in bisq_transactions.iter() { ledger.add_transaction(transaction.clone()); } ledger.transactions.sort_by_key(|transaction|transaction.date); ledger.print(); }