hledger_parser/component/
price.rs

1use chumsky::prelude::*;
2
3use crate::component::amount::{amount, Amount};
4use crate::component::whitespace::whitespace;
5use crate::state::State;
6
7#[allow(clippy::module_name_repetitions)]
8#[derive(Clone, Debug, PartialEq)]
9pub enum AmountPrice {
10    Unit(Amount),
11    Total(Amount),
12}
13
14#[allow(clippy::module_name_repetitions)]
15pub fn amount_price<'a>(
16) -> impl Parser<'a, &'a str, AmountPrice, extra::Full<Rich<'a, char>, State, ()>> {
17    just("@")
18        .repeated()
19        .at_least(1)
20        .at_most(2)
21        .collect::<Vec<_>>()
22        .then_ignore(whitespace().repeated())
23        .then(amount())
24        .map(|(price_type, price)| {
25            if price_type.len() == 1 {
26                AmountPrice::Unit(price)
27            } else {
28                AmountPrice::Total(price)
29            }
30        })
31}
32
33#[cfg(test)]
34mod tests {
35    use rust_decimal::Decimal;
36
37    use super::*;
38
39    #[test]
40    fn total_price() {
41        let result = amount_price()
42            .then_ignore(end())
43            .parse("@@   $1.35")
44            .into_result();
45        assert_eq!(
46            result,
47            Ok(AmountPrice::Total(Amount {
48                commodity: String::from("$"),
49                quantity: Decimal::new(135, 2)
50            }))
51        );
52    }
53
54    #[test]
55    fn unit_price() {
56        let result = amount_price()
57            .then_ignore(end())
58            .parse("@   $1.35")
59            .into_result();
60        assert_eq!(
61            result,
62            Ok(AmountPrice::Unit(Amount {
63                commodity: String::from("$"),
64                quantity: Decimal::new(135, 2),
65            }))
66        );
67    }
68}