hledger_parser/directive/
price.rs1use chumsky::prelude::*;
2
3use crate::component::amount::{amount, Amount};
4use crate::component::commodity::commodity;
5use crate::component::date::simple::date;
6use crate::component::time::time;
7use crate::component::whitespace::whitespace;
8use crate::state::State;
9use crate::utils::end_of_line;
10
11#[derive(Clone, Debug, PartialEq)]
12pub struct Price {
13 pub date: chrono::NaiveDate,
14 pub commodity: String,
15 pub amount: Amount,
16}
17
18pub fn price<'a>() -> impl Parser<'a, &'a str, Price, extra::Full<Rich<'a, char>, State, ()>> {
19 just("P")
20 .ignore_then(whitespace().repeated().at_least(1))
21 .ignore_then(date())
22 .then_ignore(whitespace().repeated().at_least(1))
23 .then_ignore(time().then(whitespace().repeated().at_least(1)).or_not())
24 .then(commodity())
25 .then_ignore(whitespace().repeated().at_least(1))
26 .then(amount())
27 .then_ignore(end_of_line())
28 .map(|((date, commodity), amount)| Price {
29 date,
30 commodity,
31 amount,
32 })
33}
34
35#[cfg(test)]
36mod tests {
37 use rust_decimal::Decimal;
38
39 use super::*;
40
41 #[test]
42 fn simple() {
43 let result = price()
44 .then_ignore(end())
45 .parse("P 2009-01-01 € $1.35")
46 .into_result();
47 assert_eq!(
48 result,
49 Ok(Price {
50 date: chrono::NaiveDate::from_ymd_opt(2009, 1, 1).unwrap(),
51 commodity: String::from("€"),
52 amount: Amount {
53 quantity: Decimal::new(135, 2),
54 commodity: String::from("$"),
55 },
56 })
57 );
58 }
59
60 #[test]
61 fn with_time() {
62 let result = price()
63 .then_ignore(end())
64 .parse("P 2024-04-18 00:00:00 BTC 691747.70790400 SEK")
65 .into_result();
66 assert_eq!(
67 result,
68 Ok(Price {
69 date: chrono::NaiveDate::from_ymd_opt(2024, 4, 18).unwrap(),
70 commodity: String::from("BTC"),
71 amount: Amount {
72 quantity: Decimal::new(69_174_770_790_400, 8),
73 commodity: String::from("SEK"),
74 },
75 })
76 );
77 }
78
79 #[test]
80 fn comment() {
81 let result = price()
82 .then_ignore(end())
83 .parse("P 2009-01-01 € $1.35 ; with comment")
84 .into_result();
85 assert_eq!(
86 result,
87 Ok(Price {
88 date: chrono::NaiveDate::from_ymd_opt(2009, 1, 1).unwrap(),
89 commodity: String::from("€"),
90 amount: Amount {
91 quantity: Decimal::new(135, 2),
92 commodity: String::from("$"),
93 },
94 })
95 );
96 }
97}