|
@@ -38,7 +38,10 @@ fn ledger_parser<'a>() -> impl Parser<
|
|
|
.try_map(|s: &str, span| {
|
|
|
Ok(Spanned::new(
|
|
|
Decimal::from_str_exact(s.trim()).map_err(|e| {
|
|
|
- Rich::custom(span, format!("Failed to parse '{s}' as a decimal number: {e}"))
|
|
|
+ Rich::custom(
|
|
|
+ span,
|
|
|
+ format!("Failed to parse '{s}' as a decimal number: {e}"),
|
|
|
+ )
|
|
|
})?,
|
|
|
span,
|
|
|
))
|
|
@@ -93,6 +96,11 @@ fn ledger_parser<'a>() -> impl Parser<
|
|
|
}, span))
|
|
|
});
|
|
|
|
|
|
+ let annotation = mark('[')
|
|
|
+ .ignore_then(none_of("]\n\t").repeated().to_slice())
|
|
|
+ .then_ignore(mark(']'))
|
|
|
+ .map(|v: &str| String::from(v.trim()));
|
|
|
+
|
|
|
let entry = group((
|
|
|
chumsky::text::whitespace(),
|
|
|
datestamp,
|
|
@@ -102,19 +110,29 @@ fn ledger_parser<'a>() -> impl Parser<
|
|
|
.repeated()
|
|
|
.collect::<String>(),
|
|
|
chumsky::text::newline(),
|
|
|
+ choice((
|
|
|
+ annotation
|
|
|
+ .repeated()
|
|
|
+ .collect()
|
|
|
+ .then_ignore(chumsky::text::newline()),
|
|
|
+ empty().map(|_| vec![]),
|
|
|
+ )),
|
|
|
balance.repeated().at_least(1).collect(),
|
|
|
chumsky::text::whitespace(),
|
|
|
))
|
|
|
- .map_with(|(_, datestamp, _, _, title, _, balances, _), e| {
|
|
|
- Spanned::new(
|
|
|
- LedgerEntry {
|
|
|
- datestamp,
|
|
|
- title: (!title.is_empty()).then_some(title),
|
|
|
- balances,
|
|
|
- },
|
|
|
- e.span(),
|
|
|
- )
|
|
|
- });
|
|
|
+ .map_with(
|
|
|
+ |(_, datestamp, _, _, title, _, annotations, balances, _), e| {
|
|
|
+ Spanned::new(
|
|
|
+ LedgerEntry {
|
|
|
+ datestamp,
|
|
|
+ title: (!title.is_empty()).then_some(title),
|
|
|
+ annotations,
|
|
|
+ balances,
|
|
|
+ },
|
|
|
+ e.span(),
|
|
|
+ )
|
|
|
+ },
|
|
|
+ );
|
|
|
|
|
|
entry.repeated().collect()
|
|
|
}
|