hledger_parser/directive/
commodity.rs

1use chumsky::prelude::*;
2
3use crate::component::amount::{amount, Amount};
4use crate::component::commodity::commodity as parse_commodity;
5use crate::component::whitespace::whitespace;
6use crate::state::State;
7use crate::utils::end_of_line;
8
9#[derive(Clone, Debug, PartialEq)]
10pub enum Commodity {
11    Amount(Amount),
12    Commodity(String),
13}
14
15pub fn commodity<'a>() -> impl Parser<'a, &'a str, Commodity, extra::Full<Rich<'a, char>, State, ()>>
16{
17    just("commodity")
18        .ignore_then(whitespace().repeated().at_least(1))
19        .ignore_then(
20            amount()
21                .map(Commodity::Amount)
22                .or(parse_commodity().map(Commodity::Commodity)),
23        )
24        .then_ignore(end_of_line())
25}
26
27#[cfg(test)]
28mod tests {
29    use rust_decimal::Decimal;
30
31    use super::*;
32
33    #[test]
34    fn with_symbol() {
35        let result = commodity()
36            .then_ignore(end())
37            .parse("commodity $1000.00")
38            .into_result();
39        assert_eq!(
40            result,
41            Ok(Commodity::Amount(Amount {
42                commodity: String::from("$"),
43                quantity: Decimal::new(100_000, 2),
44            }))
45        );
46    }
47
48    #[test]
49    fn no_symbol() {
50        let result = commodity()
51            .then_ignore(end())
52            .parse("commodity 1,000,000.0000")
53            .into_result();
54        assert_eq!(
55            result,
56            Ok(Commodity::Amount(Amount {
57                commodity: String::new(),
58                quantity: Decimal::new(10_000_000_000, 4),
59            }))
60        );
61    }
62
63    #[test]
64    fn comment() {
65        let result = commodity()
66            .then_ignore(end())
67            .parse("commodity 1. USD ; with comment")
68            .into_result();
69        assert_eq!(
70            result,
71            Ok(Commodity::Amount(Amount {
72                commodity: String::from("USD"),
73                quantity: Decimal::new(1, 0),
74            }))
75        );
76    }
77
78    #[test]
79    fn just_currency() {
80        let result = commodity()
81            .then_ignore(end())
82            .parse("commodity \"AAAA 2023\"  ")
83            .into_result();
84        assert_eq!(result, Ok(Commodity::Commodity(String::from("AAAA 2023"))));
85    }
86}