From 76899e8868869fb9ebc2fd7f5e08ca90351ad047 Mon Sep 17 00:00:00 2001 From: Peter Nirschl Date: Thu, 12 Dec 2024 21:31:20 +0100 Subject: [PATCH 1/3] fix typo in ImportError::HledgerExecution rename to HledgerExecution Signed-off-by: Peter Nirschl --- src/error.rs | 2 +- src/hledger/deduplication.rs | 2 +- src/hledger/query.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 27399d5..a1fbdb5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,7 @@ use thiserror::Error; #[derive(Debug, Error)] pub enum ImportError { #[error("Failed to interact with hledger: {0}")] - HledgerExection(#[from] std::io::Error), + HledgerExecution(#[from] std::io::Error), #[error("Encoding or conversion error: {0}")] StringConversion(#[from] std::str::Utf8Error), #[error("Failed to provide the path to the configruation file. Please provide the path to the configuration file in the environment variable \"HLEDGER_IMPORT_CONFIG\" to fix this error.")] diff --git a/src/hledger/deduplication.rs b/src/hledger/deduplication.rs index 1ce7188..44b9bf5 100644 --- a/src/hledger/deduplication.rs +++ b/src/hledger/deduplication.rs @@ -8,7 +8,7 @@ pub fn get_hledger_codes(config: &HledgerConfig) -> Result> { let output = Command::new(&config.path).arg("codes").output(); let output = match output { Ok(o) => o, - Err(e) => return Err(ImportError::HledgerExection(e)), + Err(e) => return Err(ImportError::HledgerExecution(e)), }; let codes = match std::str::from_utf8(&output.stdout) { diff --git a/src/hledger/query.rs b/src/hledger/query.rs index a43d1b1..1d62886 100644 --- a/src/hledger/query.rs +++ b/src/hledger/query.rs @@ -115,7 +115,7 @@ pub fn query_hledger_by_payee_and_account( let output = match output { Ok(o) => o, - Err(e) => return Err(ImportError::HledgerExection(e)), + Err(e) => return Err(ImportError::HledgerExecution(e)), }; let json_str = match std::str::from_utf8(&output.stdout) { From e13b2ac4ffd41a5c7cca39fa96a9dd77232e01ce Mon Sep 17 00:00:00 2001 From: Peter Nirschl Date: Thu, 12 Dec 2024 22:19:18 +0100 Subject: [PATCH 2/3] use hledger print for output formatting closes #12 Signed-off-by: Peter Nirschl --- src/hledger/format.rs | 32 ++++++++++++++++++++++++++++++++ src/hledger/mod.rs | 1 + src/hledger/output.rs | 31 ++++++++++++++++--------------- src/main.rs | 15 +++++++++++++-- 4 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 src/hledger/format.rs diff --git a/src/hledger/format.rs b/src/hledger/format.rs new file mode 100644 index 0000000..695412d --- /dev/null +++ b/src/hledger/format.rs @@ -0,0 +1,32 @@ +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +use crate::{config::HledgerConfig, error::*}; + +pub fn hledger_format(config: &HledgerConfig, transactions: &str) -> Result { + let mut process = Command::new(&config.path) + .arg("print") + .arg("-x") + .arg("-f-") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .map_err(ImportError::HledgerExecution)?; + + if let Some(mut stdin) = process.stdin.take() { + stdin + .write_all(transactions.as_bytes()) + .map_err(ImportError::HledgerExecution)?; + } + + let mut output = String::new(); + if let Some(mut stdout) = process.stdout.take() { + stdout + .read_to_string(&mut output) + .map_err(ImportError::HledgerExecution)?; + } + + process.wait().map_err(ImportError::HledgerExecution)?; + + Ok(output) +} diff --git a/src/hledger/mod.rs b/src/hledger/mod.rs index 6dd4a7d..8b5339c 100644 --- a/src/hledger/mod.rs +++ b/src/hledger/mod.rs @@ -1,3 +1,4 @@ pub mod deduplication; +pub mod format; pub mod output; pub mod query; diff --git a/src/hledger/output.rs b/src/hledger/output.rs index 886faee..620c991 100644 --- a/src/hledger/output.rs +++ b/src/hledger/output.rs @@ -153,10 +153,10 @@ impl Display for Transaction { result = format!("{} | {}", &result, note); } if let Some(comment) = &self.comment { - result = format!("{}\n ; {}", &result, comment); + result = format!("{}\n ; {}", &result, comment); } self.tags.iter().for_each(|tag| { - result = format!("{}\n ; {}", &result, tag); + result = format!("{}\n ; {}", &result, tag); }); self.postings.iter().for_each(|p| { result = format!("{}\n{}", &result, p); @@ -178,17 +178,15 @@ impl Display for Posting { let mut render = match &self.amount { Some(amount) => { let amount = amount.to_string(); - // 80 is the default line length - the amount should be aligned to the right (at position 80) - let length_filler = 80 - 2 - amount.len() - 1; - format!(" {: format!(" {}", &self.account), + None => format!(" {}", &self.account), }; if let Some(comment) = &self.comment { - render = format!("{}\n ; {}", &render, comment); + render = format!("{}\n ; {}", &render, comment); } self.tags.iter().for_each(|tag| { - render = format!("{}\n ; {}", &render, tag); + render = format!("{}\n ; {}", &render, tag); }); write!(f, "{}", &render) } @@ -318,7 +316,10 @@ mod tests { ], }; let result = posting.to_string(); - assert_eq!(result, " Assets:Cash -11,44 EUR\n ; lunch:\n ; valuation: 2024-05-02"); + assert_eq!( + result, + " Assets:Cash -11,44 EUR\n ; lunch:\n ; valuation: 2024-05-02" + ); let posting = Posting { account: String::from("Expenses:Groceries"), @@ -327,7 +328,7 @@ mod tests { tags: vec![], }; let result = posting.to_string(); - assert_eq!(result, " Expenses:Groceries"); + assert_eq!(result, " Expenses:Groceries"); let posting = Posting { account: String::from("Expenses:Groceries"), @@ -336,7 +337,7 @@ mod tests { tags: vec![], }; let result = posting.to_string(); - assert_eq!(result, " Expenses:Groceries\n ; test comment"); + assert_eq!(result, " Expenses:Groceries\n ; test comment"); } #[test] @@ -352,7 +353,7 @@ mod tests { postings: vec![], }; let result = t.to_string(); - assert_eq!(result, "2024-11-22 * (ABC123) Test | Note\n ; comment"); + assert_eq!(result, "2024-11-22 * (ABC123) Test | Note\n ; comment"); let t = Transaction { date: NaiveDate::from_ymd_opt(2024, 11, 22).unwrap(), @@ -370,7 +371,7 @@ mod tests { let result = t.to_string(); assert_eq!( result, - "2024-11-22 * (ABC123) Test | Note\n ; comment\n ; lunch:\n ; foo: bar" + "2024-11-22 * (ABC123) Test | Note\n ; comment\n ; lunch:\n ; foo: bar" ); let t = Transaction { @@ -416,7 +417,7 @@ mod tests { ], }; let result = t.to_string(); - assert_eq!(result, "2020-06-18 * (123-XYZ-321) Store | Bought something\n ; this is a test\n Assets:Cash -2.799,97 EUR\n Expenses:Test\n ; Some test"); + assert_eq!(result, "2020-06-18 * (123-XYZ-321) Store | Bought something\n ; this is a test\n Assets:Cash -2.799,97 EUR\n Expenses:Test\n ; Some test"); let t = Transaction { date: NaiveDate::from_ymd_opt(2020, 6, 18).unwrap(), @@ -445,7 +446,7 @@ mod tests { ], }; let result = t.to_string(); - assert_eq!(result, "2020-06-18 * Store | Bought something\n ; this is a test\n Assets:Cash -2.799,97 EUR\n Expenses:Test\n ; Some test"); + assert_eq!(result, "2020-06-18 * Store | Bought something\n ; this is a test\n Assets:Cash -2.799,97 EUR\n Expenses:Test\n ; Some test"); } #[test] diff --git a/src/main.rs b/src/main.rs index f2b442f..ef7d5f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use crate::hledger::output::Transaction; use clap::{command, Parser, ValueEnum}; use config::ImporterConfig; use error::Result; -use hledger::output::HeaderComment; +use hledger::{format::hledger_format, output::HeaderComment}; pub mod config; pub mod error; @@ -108,8 +108,19 @@ fn main() { let importer: Box = args.file_type.into(); match importer.parse(&args.input_file, &config, &codes) { Ok(transactions) => { + let transactions: Vec = transactions.iter().map(|t| t.to_string()).collect(); + let transactions = transactions.join("\n"); + + let transactions = match hledger_format(&config.hledger, &transactions) { + Ok(t) => t, + Err(e) => { + eprintln!("[ERROR] {}", e); + return; + } + }; + println!("{}", HeaderComment::new(importer.output_title())); - transactions.iter().for_each(|t| println!("{}\n", t)); + println!("{}", transactions); println!(); } Err(e) => { From 60810638f4b7bbbfdc71979a2746cd12c0a61236 Mon Sep 17 00:00:00 2001 From: Peter Nirschl Date: Thu, 12 Dec 2024 22:22:50 +0100 Subject: [PATCH 3/3] fix clippy warnings remove unused lifetimes Signed-off-by: Peter Nirschl --- src/hledger/output.rs | 2 +- src/importers/flatex_inv.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hledger/output.rs b/src/hledger/output.rs index 620c991..99f7d96 100644 --- a/src/hledger/output.rs +++ b/src/hledger/output.rs @@ -203,7 +203,7 @@ impl<'a> HeaderComment<'a> { } } -impl<'a> Display for HeaderComment<'a> { +impl Display for HeaderComment<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let asterisk_line: String = "*".repeat(78); let date_time = chrono::Local::now().to_rfc2822(); diff --git a/src/importers/flatex_inv.rs b/src/importers/flatex_inv.rs index 17c658a..6c1208e 100644 --- a/src/importers/flatex_inv.rs +++ b/src/importers/flatex_inv.rs @@ -246,7 +246,7 @@ impl<'a> FlatexPdfRegexMatcher<'a> { } } -impl<'a> TryInto for FlatexPdfRegexMatcher<'a> { +impl TryInto for FlatexPdfRegexMatcher<'_> { type Error = ImportError; fn try_into(self) -> std::prelude::v1::Result { @@ -259,7 +259,7 @@ impl<'a> TryInto for FlatexPdfRegexMatcher<'a> { } } -impl<'a> TryInto for FlatexPdfRegexMatcher<'a> { +impl TryInto for FlatexPdfRegexMatcher<'_> { type Error = ImportError; fn try_into(self) -> std::prelude::v1::Result { @@ -299,7 +299,7 @@ impl<'a> TryInto for FlatexPdfRegexMatcher<'a> { } } -impl<'a> TryInto for FlatexPdfRegexMatcher<'a> { +impl TryInto for FlatexPdfRegexMatcher<'_> { type Error = ImportError; fn try_into(self) -> std::prelude::v1::Result {