main.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. use chrono::{Date, Utc};
  2. use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
  3. use qif_generator::{
  4. account::{Account, AccountType},
  5. split::Split,
  6. transaction::Transaction,
  7. };
  8. use radix_trie::Trie;
  9. use shellexpand::tilde;
  10. use std::fs;
  11. use std::path::{Path, PathBuf};
  12. use std::time::Duration;
  13. use structopt::StructOpt;
  14. mod categories;
  15. use categories::get_category;
  16. use categories::CatStats;
  17. mod import;
  18. mod receipt;
  19. mod ui;
  20. fn read_receipt(f: &str) -> receipt::Receipt {
  21. let json = fs::read_to_string(f).expect("Can't read file");
  22. receipt::parse_receipt(&json)
  23. }
  24. #[cfg(test)]
  25. mod tests {
  26. use super::*;
  27. #[test]
  28. fn test_read_receipt() {
  29. let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
  30. p.push("tests/resources/test.json");
  31. let full_path = p.to_string_lossy();
  32. let result = read_receipt(&full_path).items;
  33. assert_eq!(result[0].name, "ХРЕН РУССКИЙ 170Г");
  34. assert_eq!(result[0].sum, 5549);
  35. }
  36. }
  37. fn gen_splits(items: &[receipt::Item], cs: &mut CatStats) -> Vec<Split> {
  38. let mut result: Vec<Split> = Vec::new();
  39. for i in items.iter() {
  40. let t = Split::new()
  41. .memo(i.name.as_str())
  42. .amount(i.sum)
  43. .category(&get_category(i.name.as_str(), cs))
  44. .build();
  45. result.push(t);
  46. }
  47. result
  48. }
  49. fn gen_trans<'a>(
  50. acc: &'a Account,
  51. date: Date<Utc>,
  52. sum: i64,
  53. splits: &'a [Split],
  54. ) -> Result<Transaction<'a>, String> {
  55. let t = Transaction::new(acc)
  56. .date(date)
  57. .memo("New")
  58. .splits(splits)
  59. .build();
  60. match t {
  61. Ok(t) => {
  62. if t.sum() == sum {
  63. Ok(t)
  64. } else {
  65. Err(format!(
  66. "Total sum is wrong. Expected: {} Got: {}",
  67. sum,
  68. t.sum()
  69. ))
  70. }
  71. }
  72. Err(e) => Err(e),
  73. }
  74. }
  75. /// Search for a pattern in a file and display the lines that contain it.
  76. #[derive(StructOpt)]
  77. struct Cli {
  78. /// The path to the file to read
  79. filename: String,
  80. #[structopt(parse(from_os_str), long, help = "Accounts csv file")]
  81. accounts: Option<PathBuf>,
  82. #[structopt(short, long, default_value = "~/.config/receqif/rc.db")]
  83. database: String,
  84. }
  85. #[cfg(not(tarpaulin_include))]
  86. fn main() {
  87. let args = Cli::from_args();
  88. let confpath: &str = &tilde(&args.database);
  89. let confpath = PathBuf::from(confpath);
  90. let ten_sec = Duration::from_secs(10);
  91. let db = PickleDb::load(
  92. &confpath,
  93. PickleDbDumpPolicy::PeriodicDump(ten_sec),
  94. SerializationMethod::Json,
  95. );
  96. let mut db = match db {
  97. Ok(db) => db,
  98. Err(_) => PickleDb::new(
  99. &confpath,
  100. PickleDbDumpPolicy::PeriodicDump(ten_sec),
  101. SerializationMethod::Json,
  102. ),
  103. };
  104. let mut catmap: CatStats = match db.get("catmap") {
  105. Some(v) => v,
  106. None => Trie::new(),
  107. };
  108. if let Some(filename) = args.accounts {
  109. let accounts = import::read_accounts(Path::new(&filename)).unwrap();
  110. db.set("accounts", &accounts).unwrap();
  111. }
  112. let receipt = read_receipt(&args.filename);
  113. let splits = gen_splits(&receipt.items, &mut catmap);
  114. let acc = Account::new()
  115. .name("Wallet")
  116. .account_type(AccountType::Cash)
  117. .build();
  118. let t = gen_trans(&acc, receipt.date(), receipt.total_sum(), &splits).unwrap();
  119. print!("{}", acc.to_string());
  120. println!("{}", t.to_string());
  121. db.set("catmap", &catmap).unwrap();
  122. db.dump().unwrap();
  123. ui::run_tv();
  124. }