use crate::{
parse::{parse_ledger, ParseError},
repl::display::DisplayContext,
};
use std::io::{Read, Write};
#[derive(thiserror::Error, Debug)]
pub enum FormatError {
#[error("failed to perform IO")]
IO(#[from] std::io::Error),
#[error("failed to parse the file")]
Parse(#[from] Box<ParseError>),
#[error("recursive format isn't supported yet")]
UnsupportedRecursiveFormat,
}
#[derive(Debug, Default)]
pub struct FormatOptions {
recursive: bool,
}
impl FormatOptions {
pub fn new() -> Self {
Self::default()
}
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
pub fn format<R, W>(&self, r: &mut R, w: &mut W) -> Result<(), FormatError>
where
R: Read,
W: Write,
{
if self.recursive {
return Err(FormatError::UnsupportedRecursiveFormat);
}
let mut buf = String::new();
r.read_to_string(&mut buf)?;
let ctx = DisplayContext::default();
for txn in parse_ledger(&buf) {
let txn = txn.map_err(Box::new)?;
writeln!(w, "{}", ctx.as_display(&txn))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
#[test]
fn format_succeeds_transaction_without_lot_price() {
let input = indoc! {"
; Top
; level
#comment
%can
|have several prefixes.
; second
; round
account Foo\t
alias Bar\t
note これは何でしょうか
alias Baz
commodity USD\t
\talias 米ドル\t
\talias $\t
apply tag foo
apply tag key: value
apply tag key:: 10 USD
end apply tag
end apply tag
end apply tag
include path/to/other.ledger
2021/03/12 Opening Balance ; initial balance
Assets:Bank = 1000 CHF
Equity
2021/05/14 !(#txn-1) My Grocery
Expenses:Grocery\t10 CHF
Expenses:Commissions 1 USD @ 0.98 CHF ; Payee: My Card
; My card took commission
; :financial:経済:
Assets:Bank -20 CHF=1CHF
Expenses:Household = 0
Assets:Complex (-10 * 2.1 $) @ (1 $ + 1 $) = 2.5 $
Assets:Broker -2 SPINX (bought before Xmas) {100 USD} [2010/12/23] @ 10000 USD
Liabilities:Comma 5,678.00 CHF @ 1,000,000 JPYRIN = -123,456.12 CHF
"};
let want = indoc! {"
; Top
; level
;comment
;can
;have several prefixes.
; second
; round
account Foo
alias Bar
note これは何でしょうか
alias Baz
commodity USD
alias 米ドル
alias $
apply tag foo
apply tag key: value
apply tag key:: 10 USD
end apply tag
end apply tag
end apply tag
include path/to/other.ledger
2021/03/12 Opening Balance
; initial balance
Assets:Bank = 1000 CHF
Equity
2021/05/14 ! (#txn-1) My Grocery
Expenses:Grocery 10 CHF
Expenses:Commissions 1 USD @ 0.98 CHF
; Payee: My Card
; My card took commission
; :financial:経済:
Assets:Bank -20 CHF = 1 CHF
Expenses:Household = 0
Assets:Complex (-10 * 2.1 $) @ (1 $ + 1 $) = 2.5 $
Assets:Broker -2 SPINX {100 USD} [2010/12/23] (bought before Xmas) @ 10000 USD
Liabilities:Comma 5,678.00 CHF @ 1,000,000 JPYRIN = -123,456.12 CHF
"};
let mut output = Vec::new();
let mut r = input.as_bytes();
FormatOptions::new()
.format(&mut r, &mut output)
.expect("format() should succeeds");
let got = std::str::from_utf8(&output).expect("output should be valid UTF-8");
assert_eq!(want, got);
}
}