1use crate::{format::*, options::BeancountOption};
2use chumsky::{
3 extra::ParserExtra,
4 input::{Input, MapExtra},
5};
6use rust_decimal::Decimal;
7use std::{
8 cmp::max,
9 collections::{HashMap, HashSet, hash_map},
10 fmt::{self, Display, Formatter},
11 hash::{Hash, Hasher},
12 iter::empty,
13 mem::swap,
14 ops::Deref,
15};
16use std::{marker::PhantomData, ops::DerefMut};
17use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
18use time::Date;
19
20pub trait Report {
22 fn message(&self) -> &str;
23 fn reason(&self) -> &str;
24 fn span(&self) -> Span;
25 fn contexts(&self) -> impl Iterator<Item = (&str, Span)>;
26 fn related(&self) -> impl Iterator<Item = (&str, Span)>;
27}
28
29#[derive(Clone, Debug)]
31pub struct ErrorOrWarning<K>(pub(crate) Box<ErrorOrWarningImpl<K>>);
32
33impl<K> From<ErrorOrWarningImpl<K>> for ErrorOrWarning<K> {
34 fn from(value: ErrorOrWarningImpl<K>) -> Self {
35 Self(Box::new(value))
36 }
37}
38
39#[derive(Clone, Debug)]
41pub(crate) struct ErrorOrWarningImpl<K> {
42 pub(crate) message: String,
43 pub(crate) reason: String,
44 pub(crate) span: Span,
45 pub(crate) contexts: Option<Vec<(String, Span)>>,
46 pub(crate) related: Option<Vec<(String, Span)>>,
47 kind: PhantomData<K>,
48}
49
50#[derive(Clone, Debug)]
52pub struct ErrorKind;
53
54pub type Error = ErrorOrWarning<ErrorKind>;
57
58#[derive(Clone, Debug)]
60pub struct WarningKind;
61
62pub type Warning = ErrorOrWarning<WarningKind>;
65
66impl<K> Report for ErrorOrWarning<K> {
67 fn message(&self) -> &str {
68 &self.0.message
69 }
70
71 fn reason(&self) -> &str {
72 &self.0.reason
73 }
74
75 fn span(&self) -> Span {
76 self.0.span
77 }
78
79 fn contexts(&self) -> impl Iterator<Item = (&str, Span)> {
80 self.0
81 .contexts
82 .iter()
83 .flatten()
84 .map(|(element, span)| (element.as_str(), *span))
85 }
86
87 fn related(&self) -> impl Iterator<Item = (&str, Span)> {
88 self.0
89 .related
90 .iter()
91 .flatten()
92 .map(|(element, span)| (element.as_str(), *span))
93 }
94}
95
96impl<K> ErrorOrWarning<K>
97where
98 K: ErrorOrWarningKind,
99{
100 pub(crate) fn new<M: Into<String>, R: Into<String>>(message: M, reason: R, span: Span) -> Self {
102 ErrorOrWarningImpl::<K> {
103 message: message.into(),
104 reason: reason.into(),
105 span,
106 contexts: None,
107 related: None,
108 kind: PhantomData,
109 }
110 .into()
111 }
112
113 pub fn related_to<'a, 'b, T>(self, element: &'b Spanned<T>) -> Self
115 where
116 T: ElementType<'a> + 'b,
117 {
118 let mut e = self;
119 let related = e.0.related.get_or_insert(Vec::new());
120 related.push((element.element_type().to_string(), element.span));
121 e
122 }
123
124 pub fn related_to_all<'a, 'b, T, I>(self, elements: I) -> Self
126 where
127 T: ElementType<'a> + 'b,
128 I: IntoIterator<Item = &'b Spanned<T>>,
129 {
130 let mut new_related = elements
131 .into_iter()
132 .map(|element| (element.element_type().to_string(), element.span))
133 .collect::<Vec<_>>();
134
135 let mut e = self;
136 let related = e.0.related.get_or_insert(Vec::new());
137 related.append(&mut new_related);
138 e
139 }
140
141 pub(crate) fn related_to_named_span<S>(self, name: S, span: Span) -> Self
142 where
143 S: ToString,
144 {
145 let mut e = self;
146 let related = e.0.related.get_or_insert(Vec::new());
147 related.push((name.to_string(), span));
148 e
149 }
150
151 pub fn in_context<'a, T>(self, element: &Spanned<T>) -> Self
154 where
155 T: ElementType<'a>,
156 {
157 let mut e = self;
158 let contexts = e.0.contexts.get_or_insert(Vec::new());
159 contexts.push((element.element_type().to_string(), element.span));
160 e
161 }
162
163 pub fn in_contexts<'a, 'b, T, I>(self, elements: I) -> Self
165 where
166 T: ElementType<'a> + 'b,
167 I: IntoIterator<Item = &'b Spanned<T>>,
168 {
169 let mut new_contexts = elements
170 .into_iter()
171 .map(|element| (element.element_type().to_string(), element.span))
172 .collect::<Vec<_>>();
173
174 let mut e = self;
175 let contexts = e.0.contexts.get_or_insert(Vec::new());
176 contexts.append(&mut new_contexts);
177 e
178 }
179
180 pub(crate) fn in_explicitly_labelled_contexts<I>(self, elements: I) -> Self
182 where
183 I: IntoIterator<Item = (String, Span)>,
184 {
185 let mut new_contexts = elements.into_iter().collect::<Vec<_>>();
186
187 let mut e = self;
188 let contexts = e.0.contexts.get_or_insert(Vec::new());
189 contexts.append(&mut new_contexts);
190 e
191 }
192
193 pub fn with_annotation<S>(self, annotation: S) -> AnnotatedErrorOrWarning<K>
194 where
195 S: Into<String>,
196 {
197 AnnotatedErrorOrWarning {
198 error_or_warning: self,
199 annotation: Some(annotation.into()),
200 }
201 }
202}
203
204impl<K> ErrorOrWarningImpl<K>
205where
206 K: ErrorOrWarningKind,
207{
208 pub(crate) fn color(&self) -> ariadne::Color {
209 K::color()
210 }
211
212 pub(crate) fn report_kind(&self) -> ariadne::ReportKind<'static> {
213 K::report_kind()
214 }
215}
216
217fn fmt_error_or_warning<K>(value: &ErrorOrWarning<K>, f: &mut Formatter<'_>) -> fmt::Result
218where
219 K: ErrorOrWarningKind,
220{
221 write!(f, "{} ({}) ", value.0.message, value.0.reason)?;
222 write!(
223 f,
224 "at {}..{} of source {}",
225 value.0.span.start, value.0.span.end, value.0.span.source
226 )?;
227 if let Some(contexts) = &value.0.contexts {
228 for context in contexts.iter() {
229 write!(f, " while parsing {} at {:?}", context.0, context.1)?;
230 }
231 }
232 Ok(())
233}
234
235impl Display for Error {
236 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
237 fmt_error_or_warning(self, f)
238 }
239}
240
241impl std::error::Error for Error {}
242
243impl Display for Warning {
244 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
245 fmt_error_or_warning(self, f)
246 }
247}
248
249pub trait ErrorOrWarningKind {
251 fn report_kind() -> ariadne::ReportKind<'static>;
252
253 fn color() -> ariadne::Color;
254}
255
256impl ErrorOrWarningKind for ErrorKind {
257 fn report_kind() -> ariadne::ReportKind<'static> {
258 ariadne::ReportKind::Error
259 }
260
261 fn color() -> ariadne::Color {
262 ariadne::Color::Red
263 }
264}
265
266impl ErrorOrWarningKind for WarningKind {
267 fn report_kind() -> ariadne::ReportKind<'static> {
268 ariadne::ReportKind::Warning
269 }
270
271 fn color() -> ariadne::Color {
272 ariadne::Color::Yellow
273 }
274}
275
276#[derive(Clone, Debug)]
278pub struct AnnotatedErrorOrWarning<K>
279where
280 K: ErrorOrWarningKind,
281{
282 pub error_or_warning: ErrorOrWarning<K>,
283 pub annotation: Option<String>,
284}
285
286impl<K> From<ErrorOrWarning<K>> for AnnotatedErrorOrWarning<K>
287where
288 K: ErrorOrWarningKind,
289{
290 fn from(error_or_warning: ErrorOrWarning<K>) -> Self {
291 Self {
292 error_or_warning,
293 annotation: None,
294 }
295 }
296}
297
298impl<K> Deref for AnnotatedErrorOrWarning<K>
299where
300 K: ErrorOrWarningKind,
301{
302 type Target = ErrorOrWarning<K>;
303
304 fn deref(&self) -> &Self::Target {
305 &self.error_or_warning
306 }
307}
308
309impl<K> AnnotatedErrorOrWarning<K>
310where
311 K: ErrorOrWarningKind,
312{
313 pub fn annotation(&self) -> Option<&str> {
314 self.annotation.as_deref()
315 }
316}
317
318pub type AnnotatedError = AnnotatedErrorOrWarning<ErrorKind>;
319pub type AnnotatedWarning = AnnotatedErrorOrWarning<WarningKind>;
320
321pub struct SpannedSource<'a> {
323 pub file_name: Option<&'a str>,
325 pub start_line: usize,
326 pub end_line: usize,
327 pub content: &'a str,
328}
329
330#[derive(PartialEq, Eq, Hash, Clone, Copy, EnumString, EnumIter, IntoStaticStr, Debug)]
332pub enum AccountType {
333 Assets,
334 Liabilities,
335 Equity,
336 Income,
337 Expenses,
338}
339
340impl AsRef<str> for AccountType {
341 fn as_ref(&self) -> &'static str {
342 self.into()
343 }
344}
345
346#[derive(PartialEq, Eq, Default, Clone, Copy, Debug)]
348pub enum Flag {
349 #[default]
350 Asterisk,
351 Exclamation,
352 Ampersand,
353 Hash,
354 Question,
355 Percent,
356 Letter(FlagLetter),
357}
358
359impl ElementType<'static> for Flag {
360 fn element_type(&self) -> &'static str {
361 "flag"
362 }
363}
364
365impl Display for Flag {
366 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
367 use Flag::*;
368 let (prefix, c) = match self {
369 Asterisk => (None, '*'),
370 Exclamation => (None, '!'),
371 Ampersand => (None, '&'),
372 Hash => (None, '#'),
373 Question => (None, '?'),
374 Percent => (None, '%'),
375 Letter(FlagLetter(c)) => (Some('\''), *c),
376 };
377
378 match prefix {
379 Some(prefix) => write!(f, "{}{}", prefix, c),
380 None => write!(f, "{}", c),
381 }
382 }
383}
384
385#[derive(PartialEq, Eq, Copy, Clone, Debug)]
387pub struct FlagLetter(char);
388
389impl FlagLetter {
390 pub fn char(&self) -> char {
392 self.0
393 }
394
395 pub(crate) fn is_valid(c: &char) -> bool {
396 c.is_ascii_uppercase()
397 }
398}
399
400#[derive(PartialEq, Eq, Debug)]
402pub struct FlagLetterError(char);
403
404impl Display for FlagLetterError {
405 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
406 write!(
407 f,
408 "invalid character '{}' for flag letter - must be uppercase ASCII",
409 self.0
410 )
411 }
412}
413
414impl std::error::Error for FlagLetterError {}
415
416impl TryFrom<char> for FlagLetter {
417 type Error = FlagLetterError;
418
419 fn try_from(c: char) -> Result<Self, Self::Error> {
420 if FlagLetter::is_valid(&c) {
421 Ok(FlagLetter(c))
422 } else {
423 Err(FlagLetterError(c))
424 }
425 }
426}
427
428#[derive(
430 EnumString, EnumIter, IntoStaticStr, PartialEq, Eq, Default, Clone, Copy, Display, Debug,
431)]
432#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
433pub enum Booking {
434 #[default]
435 Strict,
436 StrictWithSize,
437 None,
438 Average,
439 Fifo,
440 Lifo,
441 Hifo,
442}
443
444impl AsRef<str> for Booking {
445 fn as_ref(&self) -> &'static str {
446 self.into()
447 }
448}
449
450impl ElementType<'static> for Booking {
451 fn element_type(&self) -> &'static str {
452 "booking"
453 }
454}
455
456#[derive(
457 EnumString, EnumIter, IntoStaticStr, PartialEq, Eq, Default, Clone, Copy, Display, Debug,
458)]
459#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
460pub enum PluginProcessingMode {
461 #[default]
462 Default,
463 Raw,
464}
465
466#[derive(PartialEq, Eq, Copy, Clone, Default, Debug)]
468pub struct SourceId(u32);
469
470impl From<usize> for SourceId {
471 fn from(value: usize) -> Self {
472 SourceId(value as u32)
473 }
474}
475
476impl From<SourceId> for usize {
477 fn from(value: SourceId) -> Self {
478 value.0 as usize
479 }
480}
481
482impl Display for SourceId {
483 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
484 write!(f, "{}", self.0)
485 }
486}
487
488pub(crate) type Span_ = chumsky::span::SimpleSpan<usize, SourceId>;
490
491#[derive(PartialEq, Eq, Copy, Clone, Debug)]
493pub struct Span {
494 pub source: usize,
495 pub start: usize,
496 pub end: usize,
497}
498
499impl From<&Span_> for Span {
500 fn from(value: &Span_) -> Self {
501 Span {
502 source: value.context.into(),
503 start: value.start,
504 end: value.end,
505 }
506 }
507}
508
509impl From<Span_> for Span {
510 fn from(value: Span_) -> Self {
511 Span {
512 source: value.context.into(),
513 start: value.start,
514 end: value.end,
515 }
516 }
517}
518
519impl From<Span> for Span_ {
520 fn from(value: Span) -> Self {
521 chumsky::span::SimpleSpan {
522 start: value.start,
523 end: value.end,
524 context: value.source.into(),
525 }
526 }
527}
528
529#[derive(Clone, Debug)]
532pub struct Spanned<T> {
533 pub(crate) item: T,
534 pub(crate) span: Span,
535}
536
537impl<T> Deref for Spanned<T> {
539 type Target = T;
540
541 fn deref(&self) -> &Self::Target {
542 &self.item
543 }
544}
545
546impl<T> DerefMut for Spanned<T> {
548 fn deref_mut(&mut self) -> &mut Self::Target {
549 &mut self.item
550 }
551}
552
553pub(crate) fn spanned_<T>(item: T, span: Span_) -> Spanned<T> {
554 Spanned {
555 item,
556 span: span.into(),
557 }
558}
559
560pub fn spanned<T>(item: T, span: Span) -> Spanned<T> {
561 Spanned { item, span }
562}
563
564pub(crate) fn spanned_extra<'a, 'b, T, I, E>(item: T, e: &mut MapExtra<'a, 'b, I, E>) -> Spanned<T>
566where
567 I: Input<'a, Span = Span_>,
568 E: ParserExtra<'a, I>,
569{
570 Spanned {
571 item,
572 span: e.span().into(),
573 }
574}
575
576impl<T> Spanned<T> {
577 pub fn item(&self) -> &T {
579 &self.item
580 }
581
582 pub fn span(&self) -> &Span {
584 &self.span
585 }
586
587 pub fn as_ref(&self) -> Spanned<&T> {
589 Spanned {
590 item: &self.item,
591 span: self.span,
592 }
593 }
594
595 pub fn map<U, F>(&self, f: F) -> Spanned<U>
597 where
598 F: FnOnce(&T) -> U,
599 {
600 Spanned {
601 item: f(&self.item),
602 span: self.span,
603 }
604 }
605
606 pub fn map_into<U, F>(self, f: F) -> Spanned<U>
608 where
609 F: FnOnce(T) -> U,
610 {
611 Spanned {
612 item: f(self.item),
613 span: self.span,
614 }
615 }
616}
617
618impl<'a, T> Spanned<T>
619where
620 T: ElementType<'a>,
621{
622 pub fn error<S: Into<String>>(&self, reason: S) -> Error {
624 Error::new(
625 format!("invalid {}", self.element_type()),
626 reason,
627 self.span,
628 )
629 }
630
631 pub fn warning<S: Into<String>>(&self, reason: S) -> Warning {
633 Warning::new(
634 format!("questionable {}", self.element_type()),
635 reason,
636 self.span,
637 )
638 }
639}
640
641impl<T> PartialEq for Spanned<T>
642where
643 T: PartialEq,
644{
645 fn eq(&self, other: &Self) -> bool {
646 self.item.eq(&other.item)
647 }
648}
649
650impl<T> Eq for Spanned<T> where T: Eq {}
651
652impl<T> Hash for Spanned<T>
653where
654 T: Hash,
655{
656 fn hash<H: Hasher>(&self, state: &mut H) {
657 self.item.hash(state)
658 }
659}
660
661impl<T> Copy for Spanned<T> where T: Copy {}
662
663impl<T> Display for Spanned<T>
664where
665 T: Display,
666{
667 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
668 write!(f, "{}", self.item,)
669 }
670}
671
672pub trait OptionalItem<T> {
674 fn item(&self) -> Option<&T>;
675}
676
677impl<T> OptionalItem<T> for Option<&Spanned<T>> {
678 fn item(&self) -> Option<&T> {
679 self.map(|spanned| spanned.item())
680 }
681}
682
683pub trait ElementType<'a> {
685 fn element_type(&self) -> &'a str;
686}
687
688impl<'a, T> ElementType<'a> for &T
690where
691 T: ElementType<'a>,
692{
693 fn element_type(&self) -> &'a str {
694 (**self).element_type()
695 }
696}
697
698impl<'a, T> ElementType<'a> for &mut T
699where
700 T: ElementType<'a>,
701{
702 fn element_type(&self) -> &'a str {
703 (**self).element_type()
704 }
705}
706
707impl<'a, T> ElementType<'a> for Box<T>
708where
709 T: ElementType<'a>,
710{
711 fn element_type(&self) -> &'a str {
712 (**self).element_type()
713 }
714}
715
716#[derive(PartialEq, Eq, Clone, Debug)]
717#[allow(clippy::large_enum_variant)]
720pub(crate) enum Declaration<'a> {
721 Directive(Directive<'a>),
722 Pragma(Pragma<'a>),
723}
724
725#[derive(PartialEq, Eq, Clone, Debug)]
727pub struct Directive<'a> {
728 pub(crate) date: Spanned<Date>,
729 pub(crate) metadata: Spanned<Metadata<'a>>,
730 pub(crate) variant: DirectiveVariant<'a>,
731}
732
733impl<'a> Directive<'a> {
734 pub fn date(&self) -> &Spanned<Date> {
736 &self.date
737 }
738
739 pub fn metadata(&self) -> &Metadata<'a> {
741 &self.metadata
742 }
743
744 pub fn variant(&self) -> &DirectiveVariant<'a> {
746 &self.variant
747 }
748}
749
750impl ElementType<'static> for Directive<'_> {
751 fn element_type(&self) -> &'static str {
752 use DirectiveVariant::*;
753
754 match &self.variant {
755 Transaction(_) => "transaction",
756 Price(_) => "price",
757 Balance(_) => "balance",
758 Open(_) => "open",
759 Close(_) => "close",
760 Commodity(_) => "commodity",
761 Pad(_) => "pad",
762 Document(_) => "document",
763 Note(_) => "note",
764 Event(_) => "event",
765 Query(_) => "query",
766 Custom(_) => "custom",
767 }
768 }
769}
770
771impl Display for Directive<'_> {
772 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
773 use DirectiveVariant::*;
774
775 match &self.variant {
776 Transaction(x) => x.fmt(f, self.date.item, &self.metadata),
777 Price(x) => x.fmt(f, self.date.item, &self.metadata),
778 Balance(x) => x.fmt(f, self.date.item, &self.metadata),
779 Open(x) => x.fmt(f, self.date.item, &self.metadata),
780 Close(x) => x.fmt(f, self.date.item, &self.metadata),
781 Commodity(x) => x.fmt(f, self.date.item, &self.metadata),
782 Pad(x) => x.fmt(f, self.date.item, &self.metadata),
783 Document(x) => x.fmt(f, self.date.item, &self.metadata),
784 Note(x) => x.fmt(f, self.date.item, &self.metadata),
785 Event(x) => x.fmt(f, self.date.item, &self.metadata),
786 Query(x) => x.fmt(f, self.date.item, &self.metadata),
787 Custom(x) => x.fmt(f, self.date.item, &self.metadata),
788 }
789 }
790}
791
792#[derive(PartialEq, Eq, Clone, Debug)]
794pub enum DirectiveVariant<'a> {
795 Transaction(Transaction<'a>),
796 Price(Price<'a>),
797 Balance(Balance<'a>),
798 Open(Open<'a>),
799 Close(Close<'a>),
800 Commodity(Commodity<'a>),
801 Pad(Pad<'a>),
802 Document(Document<'a>),
803 Note(Note<'a>),
804 Event(Event<'a>),
805 Query(Query<'a>),
806 Custom(Custom<'a>),
807}
808
809#[derive(PartialEq, Eq, Clone, Debug)]
810pub(crate) enum Pragma<'a> {
811 Pushtag(Spanned<Tag<'a>>),
814 Poptag(Spanned<Tag<'a>>),
815 Pushmeta(MetaKeyValue<'a>),
816 Popmeta(Spanned<Key<'a>>),
817 Include(Spanned<&'a str>),
818 Option(BeancountOption<'a>),
819 Plugin(Plugin<'a>),
820}
821
822#[derive(PartialEq, Eq, Clone, Debug)]
824pub struct Transaction<'a> {
825 pub(crate) flag: Spanned<Flag>,
826 pub(crate) payee: Option<Spanned<&'a str>>,
827 pub(crate) narration: Option<Spanned<&'a str>>,
828 pub(crate) postings: Vec<Spanned<Posting<'a>>>,
829}
830
831impl<'a> Transaction<'a> {
832 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
833 write!(f, "{} {}", date, self.flag)?;
834
835 format(f, &self.payee, double_quoted, " ", Some(" "))?;
836 format(f, &self.narration, double_quoted, " ", Some(" "))?;
837 if self.payee.is_some() && self.narration.is_none() {
838 f.write_str(r#" """#)?;
839 }
840
841 metadata.fmt_tags_links_inline(f)?;
843 metadata.fmt_keys_values(f)?;
844 format(
845 f,
846 &self.postings,
847 plain,
848 NEWLINE_INDENT,
849 Some(NEWLINE_INDENT),
850 )
851 }
852
853 pub fn flag(&self) -> &Spanned<Flag> {
855 &self.flag
856 }
857
858 pub fn payee(&self) -> Option<&Spanned<&'a str>> {
860 self.payee.as_ref()
861 }
862
863 pub fn narration(&self) -> Option<&Spanned<&'a str>> {
865 self.narration.as_ref()
866 }
867
868 pub fn postings(&self) -> impl ExactSizeIterator<Item = &Spanned<Posting<'a>>> {
870 self.postings.iter()
871 }
872}
873
874#[derive(PartialEq, Eq, Clone, Debug)]
876pub struct Price<'a> {
877 pub(crate) currency: Spanned<Currency<'a>>,
878 pub(crate) amount: Spanned<Amount<'a>>,
879}
880
881impl<'a> Price<'a> {
882 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
883 write!(f, "{} price {} {}", date, &self.currency, &self.amount)?;
884
885 metadata.fmt_tags_links_inline(f)?;
887 metadata.fmt_keys_values(f)?;
888
889 Ok(())
890 }
891
892 pub fn currency(&self) -> &Spanned<Currency<'a>> {
894 &self.currency
895 }
896
897 pub fn amount(&self) -> &Spanned<Amount<'a>> {
899 &self.amount
900 }
901}
902
903impl ElementType<'static> for Price<'_> {
904 fn element_type(&self) -> &'static str {
905 "price"
906 }
907}
908
909#[derive(PartialEq, Eq, Clone, Debug)]
911pub struct Balance<'a> {
912 pub(crate) account: Spanned<Account<'a>>,
913 pub(crate) atol: Spanned<AmountWithTolerance<'a>>,
914}
915
916impl<'a> Balance<'a> {
917 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
918 write!(f, "{} balance {} {}", date, &self.account, &self.atol)?;
919
920 metadata.fmt_tags_links_inline(f)?;
922 metadata.fmt_keys_values(f)?;
923
924 Ok(())
925 }
926
927 pub fn account(&self) -> &Spanned<Account<'a>> {
929 &self.account
930 }
931
932 pub fn atol(&self) -> &Spanned<AmountWithTolerance<'a>> {
934 &self.atol
935 }
936}
937
938impl ElementType<'static> for Balance<'_> {
939 fn element_type(&self) -> &'static str {
940 "balance"
941 }
942}
943
944#[derive(PartialEq, Eq, Clone, Debug)]
946pub struct Open<'a> {
947 pub(crate) account: Spanned<Account<'a>>,
948 pub(crate) currencies: HashSet<Spanned<Currency<'a>>>,
949 pub(crate) booking: Option<Spanned<Booking>>,
950}
951
952impl<'a> Open<'a> {
953 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
954 write!(f, "{} open {}", date, self.account)?;
955 format(f, &self.currencies, plain, ",", Some(" "))?;
956 format(f, &self.booking, double_quoted, " ", Some(" "))?;
957 metadata.fmt_tags_links_inline(f)?;
959 metadata.fmt_keys_values(f)
960 }
961
962 pub fn account(&self) -> &Spanned<Account<'a>> {
964 &self.account
965 }
966
967 pub fn currencies(&self) -> impl ExactSizeIterator<Item = &Spanned<Currency<'a>>> {
969 self.currencies.iter()
970 }
971
972 pub fn booking(&self) -> Option<&Spanned<Booking>> {
974 self.booking.as_ref()
975 }
976}
977
978#[derive(PartialEq, Eq, Clone, Debug)]
980pub struct Close<'a> {
981 pub(crate) account: Spanned<Account<'a>>,
982}
983
984impl<'a> Close<'a> {
985 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
986 write!(f, "{} close {}", date, self.account)?;
987 metadata.fmt_tags_links_inline(f)?;
989 metadata.fmt_keys_values(f)
990 }
991
992 pub fn account(&self) -> &Spanned<Account<'a>> {
994 &self.account
995 }
996}
997
998#[derive(PartialEq, Eq, Clone, Debug)]
1000pub struct Commodity<'a> {
1001 pub(crate) currency: Spanned<Currency<'a>>,
1002}
1003
1004impl<'a> Commodity<'a> {
1005 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1006 write!(f, "{} commodity {}", date, self.currency)?;
1007 metadata.fmt_tags_links_inline(f)?;
1009 metadata.fmt_keys_values(f)
1010 }
1011
1012 pub fn currency(&self) -> &Spanned<Currency<'a>> {
1014 &self.currency
1015 }
1016}
1017
1018#[derive(PartialEq, Eq, Clone, Debug)]
1020pub struct Pad<'a> {
1021 pub(crate) account: Spanned<Account<'a>>,
1022 pub(crate) source: Spanned<Account<'a>>,
1023}
1024
1025impl<'a> Pad<'a> {
1026 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1027 write!(f, "{} pad {} {}", date, self.account, self.source)?;
1028 metadata.fmt_tags_links_inline(f)?;
1030 metadata.fmt_keys_values(f)
1031 }
1032
1033 pub fn account(&self) -> &Spanned<Account<'a>> {
1035 &self.account
1036 }
1037
1038 pub fn source(&self) -> &Spanned<Account<'a>> {
1040 &self.source
1041 }
1042}
1043
1044#[derive(PartialEq, Eq, Clone, Debug)]
1046pub struct Document<'a> {
1047 pub(crate) account: Spanned<Account<'a>>,
1048 pub(crate) path: Spanned<&'a str>,
1049}
1050
1051impl<'a> Document<'a> {
1052 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1053 write!(f, "{} document {} \"{}\"", date, self.account, self.path)?;
1054 metadata.fmt_tags_links_inline(f)?;
1056 metadata.fmt_keys_values(f)
1057 }
1058
1059 pub fn account(&self) -> &Spanned<Account<'a>> {
1061 &self.account
1062 }
1063
1064 pub fn path(&self) -> &Spanned<&'a str> {
1067 &self.path
1068 }
1069}
1070
1071#[derive(PartialEq, Eq, Clone, Debug)]
1073pub struct Note<'a> {
1074 pub(crate) account: Spanned<Account<'a>>,
1075 pub(crate) comment: Spanned<&'a str>,
1076}
1077
1078impl<'a> Note<'a> {
1079 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1080 write!(f, "{} note {} \"{}\"", date, self.account, self.comment)?;
1081 metadata.fmt_tags_links_inline(f)?;
1083 metadata.fmt_keys_values(f)
1084 }
1085
1086 pub fn account(&self) -> &Spanned<Account<'a>> {
1088 &self.account
1089 }
1090
1091 pub fn comment(&self) -> &Spanned<&'a str> {
1093 &self.comment
1094 }
1095}
1096
1097#[derive(PartialEq, Eq, Clone, Debug)]
1099pub struct Event<'a> {
1100 pub(crate) event_type: Spanned<&'a str>,
1101 pub(crate) description: Spanned<&'a str>,
1102}
1103
1104impl<'a> Event<'a> {
1105 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1106 write!(
1107 f,
1108 "{} event \"{}\" \"{}\"",
1109 date, self.event_type, self.description
1110 )?;
1111 metadata.fmt_tags_links_inline(f)?;
1113 metadata.fmt_keys_values(f)
1114 }
1115
1116 pub fn event_type(&self) -> &Spanned<&'a str> {
1118 &self.event_type
1119 }
1120
1121 pub fn description(&self) -> &Spanned<&'a str> {
1123 &self.description
1124 }
1125}
1126
1127#[derive(PartialEq, Eq, Clone, Debug)]
1129pub struct Query<'a> {
1130 pub(crate) name: Spanned<&'a str>,
1131 pub(crate) content: Spanned<&'a str>,
1132}
1133
1134impl<'a> Query<'a> {
1135 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1136 write!(f, "{} query \"{}\" \"{}\"", date, self.name, self.content)?;
1137 metadata.fmt_tags_links_inline(f)?;
1139 metadata.fmt_keys_values(f)
1140 }
1141
1142 pub fn name(&self) -> &Spanned<&'a str> {
1144 &self.name
1145 }
1146
1147 pub fn content(&self) -> &Spanned<&'a str> {
1149 &self.content
1150 }
1151}
1152
1153#[derive(PartialEq, Eq, Clone, Debug)]
1155pub struct Custom<'a> {
1156 pub(crate) type_: Spanned<&'a str>,
1157 pub(crate) values: Vec<Spanned<MetaValue<'a>>>,
1158}
1159
1160impl<'a> Custom<'a> {
1161 fn fmt(&self, f: &mut Formatter<'_>, date: Date, metadata: &Metadata) -> fmt::Result {
1162 write!(f, "{} custom \"{}\"", date, self.type_)?;
1163 format(f, &self.values, plain, SPACE, Some(SPACE))?;
1164 metadata.fmt_tags_links_on_separate_lines(f)?;
1166 metadata.fmt_keys_values(f)
1167 }
1168
1169 pub fn type_(&self) -> &Spanned<&'a str> {
1171 &self.type_
1172 }
1173
1174 pub fn values(&self) -> impl ExactSizeIterator<Item = &Spanned<MetaValue<'a>>> {
1176 self.values.iter()
1177 }
1178}
1179
1180#[derive(PartialEq, Eq, Clone, Debug)]
1182pub struct Plugin<'a> {
1183 pub(crate) module_name: Spanned<&'a str>,
1184 pub(crate) config: Option<Spanned<&'a str>>,
1185}
1186
1187impl<'a> Plugin<'a> {
1188 pub fn module_name(&self) -> &Spanned<&'a str> {
1190 &self.module_name
1191 }
1192
1193 pub fn config(&self) -> Option<&Spanned<&'a str>> {
1195 self.config.as_ref()
1196 }
1197}
1198
1199impl Display for Plugin<'_> {
1200 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1201 write!(f, "plugin \"{}\"", self.module_name)?;
1202 if let Some(config) = &self.config {
1203 write!(f, " \"{}\"", config)?;
1204 }
1205 writeln!(f)
1206 }
1207}
1208
1209#[derive(PartialEq, Eq, Clone, Debug)]
1211pub struct Account<'a> {
1212 account_type: AccountType,
1213 name: &'a str,
1214}
1215
1216impl<'a> Account<'a> {
1217 pub(crate) fn new(
1219 s: &'a str,
1220 account_type_names: &AccountTypeNames,
1221 ) -> Result<Self, AccountError> {
1222 let mut account = s.split(':');
1223 let account_type_name = AccountTypeName::try_from(
1224 account
1225 .by_ref()
1226 .next()
1227 .ok_or(AccountError(AccountErrorKind::MissingColon))?,
1228 )
1229 .map_err(|e| AccountError(AccountErrorKind::TypeName(e)))?;
1230 for subaccount in account {
1231 let _ = AccountName::try_from(subaccount)
1232 .map_err(|e| AccountError(AccountErrorKind::AccountName(e)))?;
1233 }
1234
1235 match account_type_names.get(&account_type_name) {
1236 Some(account_type) => Ok(Account {
1237 account_type,
1238 name: s,
1239 }),
1240 None => Err(AccountError(AccountErrorKind::UnknownAccountType(format!(
1241 "unknown account type {}, must be one of {}",
1242 &account_type_name, account_type_names
1243 )))),
1244 }
1245 }
1246
1247 pub fn account_type(&self) -> AccountType {
1248 self.account_type
1249 }
1250}
1251
1252impl Display for Account<'_> {
1253 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1254 write!(f, "{}", &self.name)
1255 }
1256}
1257
1258impl<'a> AsRef<str> for Account<'a> {
1259 fn as_ref(&self) -> &'a str {
1260 self.name
1261 }
1262}
1263
1264impl<'a> From<&'_ Account<'a>> for &'a str {
1265 fn from(value: &Account<'a>) -> Self {
1266 value.name
1267 }
1268}
1269
1270#[derive(PartialEq, Eq, Debug)]
1272pub struct AccountError(AccountErrorKind);
1273
1274#[derive(PartialEq, Eq, Debug)]
1275enum AccountErrorKind {
1276 MissingColon,
1277 TypeName(AccountTypeNameError),
1278 AccountName(AccountNameError),
1279 UnknownAccountType(String),
1280}
1281
1282impl Display for AccountError {
1283 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1284 use AccountErrorKind::*;
1285 match &self.0 {
1286 MissingColon => f.write_str("missing colon"),
1287 TypeName(e) => write!(f, "{}", e),
1288 AccountName(e) => write!(f, "{}", e),
1289 UnknownAccountType(e) => write!(f, "{}", e),
1290 }
1291 }
1292}
1293
1294impl std::error::Error for AccountError {}
1295
1296impl ElementType<'static> for Account<'_> {
1297 fn element_type(&self) -> &'static str {
1298 "account"
1299 }
1300}
1301
1302#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1304pub struct Subaccount<'a>(&'a str);
1305
1306impl<'a> AsRef<str> for Subaccount<'a> {
1307 fn as_ref(&self) -> &'a str {
1308 self.0
1309 }
1310}
1311
1312impl<'a> From<Subaccount<'a>> for &'a str {
1313 fn from(value: Subaccount<'a>) -> Self {
1314 value.0
1315 }
1316}
1317
1318impl<'a> From<&'_ Subaccount<'a>> for &'a str {
1319 fn from(value: &Subaccount<'a>) -> Self {
1320 value.0
1321 }
1322}
1323
1324impl Display for Subaccount<'_> {
1325 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1326 write!(f, "{}", &self.0)
1327 }
1328}
1329
1330impl<'a> TryFrom<&'a str> for Subaccount<'a> {
1331 type Error = AccountNameError;
1332
1333 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1334 let mut empty = true;
1335 for account_name in s.split(':') {
1336 _ = AccountName::try_from(account_name)?;
1337 empty = false;
1338 }
1339
1340 if empty {
1341 Err(AccountNameError(AccountNameErrorKind::Empty))
1342 } else {
1343 Ok(Subaccount(s))
1344 }
1345 }
1346}
1347
1348#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1350pub struct AccountTypeName<'a>(&'a str);
1351
1352impl AccountTypeName<'_> {
1353 pub(crate) fn is_valid_initial(c: &char) -> bool {
1354 c.is_ascii_uppercase()
1355 }
1356
1357 pub(crate) fn is_valid_subsequent(c: &char) -> bool {
1358 c.is_alphanumeric() || *c == '-'
1359 }
1360}
1361
1362impl<'a> TryFrom<&'a str> for AccountTypeName<'a> {
1363 type Error = AccountTypeNameError;
1364
1365 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1366 use AccountTypeNameErrorKind::*;
1367 if s.is_empty() {
1368 Err(AccountTypeNameError(Empty))
1369 } else {
1370 let mut chars = s.chars();
1371 let initial = chars.next().unwrap();
1372 if !AccountTypeName::is_valid_initial(&initial) {
1373 Err(AccountTypeNameError(Initial(initial)))
1374 } else {
1375 let bad_chars = chars
1376 .filter(|c| !AccountTypeName::is_valid_subsequent(c))
1377 .collect::<Vec<char>>();
1378 if bad_chars.is_empty() {
1379 Ok(AccountTypeName(s))
1380 } else {
1381 Err(AccountTypeNameError(Subsequent(bad_chars)))
1382 }
1383 }
1384 }
1385 }
1386}
1387
1388impl ElementType<'static> for AccountTypeName<'_> {
1389 fn element_type(&self) -> &'static str {
1390 "account type name"
1391 }
1392}
1393
1394impl<'a> AsRef<str> for AccountTypeName<'a> {
1395 fn as_ref(&self) -> &'a str {
1396 self.0
1397 }
1398}
1399
1400impl<'a> From<AccountTypeName<'a>> for &'a str {
1401 fn from(value: AccountTypeName<'a>) -> Self {
1402 value.0
1403 }
1404}
1405
1406impl<'a> From<&'_ AccountTypeName<'a>> for &'a str {
1407 fn from(value: &AccountTypeName<'a>) -> Self {
1408 value.0
1409 }
1410}
1411
1412impl PartialEq<&str> for AccountTypeName<'_> {
1413 fn eq(&self, other: &&str) -> bool {
1414 self.0 == *other
1415 }
1416}
1417
1418impl Display for AccountTypeName<'_> {
1419 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1420 write!(f, "{}", &self.0)
1421 }
1422}
1423
1424#[derive(PartialEq, Eq, Debug)]
1426pub struct AccountTypeNameError(AccountTypeNameErrorKind);
1427
1428#[derive(PartialEq, Eq, Debug)]
1429enum AccountTypeNameErrorKind {
1430 Empty,
1431 Initial(char),
1432 Subsequent(Vec<char>),
1433}
1434
1435impl Display for AccountTypeNameError {
1436 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1437 use AccountTypeNameErrorKind::*;
1438 match &self.0 {
1439 Empty => write!(f, "empty account name"),
1440 Initial(bad_char) => write!(
1441 f,
1442 "invalid character '{}' for account name initial - must be uppercase ASCII letter",
1443 bad_char
1444 ),
1445 Subsequent(bad_chars) => {
1446 format(
1447 f,
1448 bad_chars,
1449 single_quoted,
1450 ", ",
1451 Some("invalid character "),
1452 )?;
1453 f.write_str(" in account name - must be alphanumeric or '-'")
1454 }
1455 }
1456 }
1457}
1458
1459impl std::error::Error for AccountTypeNameError {}
1460
1461#[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone, Debug)]
1463pub struct AccountName<'a>(&'a str);
1464
1465impl AccountName<'_> {
1466 pub(crate) fn is_valid_initial(c: &char) -> bool {
1467 c.is_ascii_uppercase() || c.is_ascii_digit() || (!c.is_ascii() && c.is_alphabetic())
1468 }
1469
1470 pub(crate) fn is_valid_subsequent(c: &char) -> bool {
1471 c.is_alphanumeric() || *c == '-'
1472 }
1473}
1474
1475impl ElementType<'static> for AccountName<'_> {
1476 fn element_type(&self) -> &'static str {
1477 "account name"
1478 }
1479}
1480
1481impl<'a> AsRef<str> for AccountName<'a> {
1482 fn as_ref(&self) -> &'a str {
1483 self.0
1484 }
1485}
1486
1487impl<'a> From<AccountName<'a>> for &'a str {
1488 fn from(value: AccountName<'a>) -> Self {
1489 value.0
1490 }
1491}
1492
1493impl<'a> From<&'_ AccountName<'a>> for &'a str {
1494 fn from(value: &AccountName<'a>) -> Self {
1495 value.0
1496 }
1497}
1498
1499impl PartialEq<&str> for AccountName<'_> {
1500 fn eq(&self, other: &&str) -> bool {
1501 self.0 == *other
1502 }
1503}
1504
1505impl Display for AccountName<'_> {
1506 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1507 write!(f, "{}", &self.0)
1508 }
1509}
1510
1511#[derive(PartialEq, Eq, Debug)]
1513pub struct AccountNameError(AccountNameErrorKind);
1514
1515#[derive(PartialEq, Eq, Debug)]
1516enum AccountNameErrorKind {
1517 Empty,
1518 Initial(char),
1519 Subsequent(Vec<char>),
1520}
1521
1522impl Display for AccountNameError {
1523 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1524 use AccountNameErrorKind::*;
1525 match &self.0 {
1526 Empty => write!(f, "empty account name"),
1527 Initial(bad_char) => write!(
1528 f,
1529 "invalid character '{}' for account name initial - must be uppercase ASCII letter or digit",
1530 bad_char
1531 ),
1532 Subsequent(bad_chars) => {
1533 format(
1534 f,
1535 bad_chars,
1536 single_quoted,
1537 ", ",
1538 Some("invalid character "),
1539 )?;
1540 f.write_str(" in account name - must be alphanumeric or '-'")
1541 }
1542 }
1543 }
1544}
1545
1546impl std::error::Error for AccountNameError {}
1547
1548impl<'a> TryFrom<&'a str> for AccountName<'a> {
1549 type Error = AccountNameError;
1550
1551 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1552 use AccountNameErrorKind::*;
1553 if s.is_empty() {
1554 Err(AccountNameError(Empty))
1555 } else {
1556 let mut chars = s.chars();
1557 let initial = chars.next().unwrap();
1558 if !AccountName::is_valid_initial(&initial) {
1559 Err(AccountNameError(Initial(initial)))
1560 } else {
1561 let bad_chars = chars
1562 .filter(|c| !AccountName::is_valid_subsequent(c))
1563 .collect::<Vec<char>>();
1564 if bad_chars.is_empty() {
1565 Ok(AccountName(s))
1566 } else {
1567 Err(AccountNameError(Subsequent(bad_chars)))
1568 }
1569 }
1570 }
1571 }
1572}
1573
1574#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1576pub struct Currency<'a>(&'a str);
1577
1578const CURRENCY_INTERMEDIATE_EXTRA_CHARS: [char; 4] = ['\'', '.', '_', '-'];
1580
1581impl Currency<'_> {
1582 fn is_valid_initial(c: &char) -> bool {
1583 c.is_ascii_uppercase() || *c == '/'
1584 }
1585
1586 fn is_valid_intermediate(c: &char) -> bool {
1587 c.is_ascii_uppercase()
1588 || c.is_ascii_digit()
1589 || CURRENCY_INTERMEDIATE_EXTRA_CHARS.contains(c)
1590 }
1591
1592 fn is_valid_final(c: &char) -> bool {
1593 c.is_ascii_uppercase() || c.is_ascii_digit()
1594 }
1595}
1596
1597impl ElementType<'static> for Currency<'_> {
1598 fn element_type(&self) -> &'static str {
1599 "currency"
1600 }
1601}
1602
1603impl<'a> AsRef<str> for Currency<'a> {
1604 fn as_ref(&self) -> &'a str {
1605 self.0
1606 }
1607}
1608
1609impl<'a> From<Currency<'a>> for &'a str {
1610 fn from(value: Currency<'a>) -> Self {
1611 value.0
1612 }
1613}
1614
1615impl<'a> From<&'_ Currency<'a>> for &'a str {
1616 fn from(value: &Currency<'a>) -> Self {
1617 value.0
1618 }
1619}
1620
1621impl PartialEq<&str> for Currency<'_> {
1622 fn eq(&self, other: &&str) -> bool {
1623 self.0 == *other
1624 }
1625}
1626
1627impl Display for Currency<'_> {
1628 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1629 write!(f, "{}", &self.0)
1630 }
1631}
1632
1633#[derive(PartialEq, Eq, Debug)]
1635pub struct CurrencyError(CurrencyErrorKind);
1636
1637#[derive(PartialEq, Eq, Debug)]
1638enum CurrencyErrorKind {
1639 Empty,
1640 Initial(char),
1641 Intermediate(Vec<char>),
1642 Final(char),
1643 MissingLetter,
1644}
1645
1646impl Display for CurrencyError {
1647 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1648 use CurrencyErrorKind::*;
1649 match &self.0 {
1650 Empty => write!(f, "empty currency"),
1651 Initial(bad_char) => write!(
1652 f,
1653 "invalid initial character '{}' for currency - must be uppercase ASCII letter or '/'",
1654 bad_char
1655 ),
1656 Intermediate(bad_chars) => {
1657 format(
1658 f,
1659 bad_chars,
1660 single_quoted,
1661 ", ",
1662 Some("invalid intermediate characters "),
1663 )?;
1664 format(
1665 f,
1666 CURRENCY_INTERMEDIATE_EXTRA_CHARS,
1667 single_quoted,
1668 ", ",
1669 Some(" for currency - must be upppercase ASCII alphanumeric or one of "),
1670 )
1671 }
1672 Final(bad_char) => write!(
1673 f,
1674 "invalid final character '{}' for currency - must be uppercase ASCII alphanumeric",
1675 bad_char
1676 ),
1677 MissingLetter => write!(f, "currency must contain at least one letter"),
1678 }
1679 }
1680}
1681
1682impl std::error::Error for CurrencyError {}
1683
1684impl<'a> TryFrom<&'a str> for Currency<'a> {
1685 type Error = CurrencyError;
1686
1687 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1688 use CurrencyErrorKind::*;
1689 if s.is_empty() {
1690 Err(CurrencyError(Empty))
1691 } else {
1692 let mut chars = s.chars();
1693 let first = chars.next().unwrap();
1694 let intermediate: Vec<char> = if s.len() > 2 {
1695 chars.by_ref().take(s.len() - 2).collect()
1696 } else {
1697 empty::<char>().collect()
1698 };
1699 let last = if s.len() > 1 {
1700 chars.next().unwrap()
1701 } else {
1702 first
1703 };
1704
1705 use CurrencyErrorKind::*;
1706 if !Currency::is_valid_initial(&first) {
1707 Err(CurrencyError(Initial(first)))
1708 } else if !Currency::is_valid_final(&last) {
1709 Err(CurrencyError(Final(last)))
1710 } else {
1711 let bad_intermediates = intermediate
1712 .into_iter()
1713 .filter(|c| !Currency::is_valid_intermediate(c))
1714 .collect::<Vec<char>>();
1715 if !bad_intermediates.is_empty() {
1716 Err(CurrencyError(Intermediate(bad_intermediates)))
1717 } else if s.find(|c: char| c.is_ascii_uppercase()).is_none() {
1718 Err(CurrencyError(MissingLetter))
1719 } else {
1720 Ok(Currency(s))
1721 }
1722 }
1723 }
1724 }
1725}
1726
1727#[derive(PartialEq, Eq, Clone, Debug)]
1729pub struct Posting<'a> {
1730 pub(crate) flag: Option<Spanned<Flag>>,
1731 pub(crate) account: Spanned<Account<'a>>,
1732 pub(crate) amount: Option<Spanned<ExprValue>>,
1733 pub(crate) currency: Option<Spanned<Currency<'a>>>,
1734 pub(crate) cost_spec: Option<Spanned<CostSpec<'a>>>,
1735 pub(crate) price_annotation: Option<Spanned<PriceSpec<'a>>>,
1736 pub(crate) metadata: Spanned<Metadata<'a>>,
1737}
1738
1739impl<'a> Posting<'a> {
1740 pub fn flag(&self) -> Option<&Spanned<Flag>> {
1742 self.flag.as_ref()
1743 }
1744
1745 pub fn account(&self) -> &Spanned<Account<'a>> {
1747 &self.account
1748 }
1749
1750 pub fn amount(&self) -> Option<&Spanned<ExprValue>> {
1752 self.amount.as_ref()
1753 }
1754
1755 pub fn currency(&self) -> Option<&Spanned<Currency<'a>>> {
1757 self.currency.as_ref()
1758 }
1759
1760 pub fn cost_spec(&self) -> Option<&Spanned<CostSpec<'a>>> {
1762 self.cost_spec.as_ref()
1763 }
1764
1765 pub fn price_annotation(&self) -> Option<&Spanned<PriceSpec<'a>>> {
1767 self.price_annotation.as_ref()
1768 }
1769
1770 pub fn metadata(&self) -> &Metadata<'a> {
1772 &self.metadata
1773 }
1774}
1775
1776impl ElementType<'static> for Posting<'_> {
1777 fn element_type(&self) -> &'static str {
1778 "posting"
1779 }
1780}
1781
1782impl Display for Posting<'_> {
1783 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1784 simple_format(f, self.flag, None)?;
1785
1786 write!(
1787 f,
1788 "{}{}",
1789 if self.flag.is_some() { " " } else { "" },
1790 &self.account
1791 )?;
1792
1793 simple_format(f, &self.amount, Some(" "))?;
1794 simple_format(f, self.currency, Some(" "))?;
1795 simple_format(f, &self.cost_spec, Some(" "))?;
1796 simple_format(f, &self.price_annotation, Some(" @ "))?;
1797
1798 self.metadata.fmt(f)
1799 }
1800}
1801
1802#[derive(PartialEq, Eq, Clone, Default, Debug)]
1807pub struct Metadata<'a> {
1808 pub(crate) key_values: HashMap<Spanned<Key<'a>>, Spanned<MetaValue<'a>>>,
1809 pub(crate) tags: HashSet<Spanned<Tag<'a>>>,
1810 pub(crate) links: HashSet<Spanned<Link<'a>>>,
1811}
1812
1813impl<'a> Metadata<'a> {
1814 pub fn is_empty(&self) -> bool {
1816 self.key_values().len() == 0 && self.tags().len() == 0 && self.links().len() == 0
1817 }
1818
1819 pub fn key_values(
1821 &self,
1822 ) -> impl ExactSizeIterator<Item = (&Spanned<Key<'a>>, &Spanned<MetaValue<'a>>)> {
1823 self.key_values.iter()
1824 }
1825
1826 pub fn key_value<'s, 'k>(&'s self, key: Key<'k>) -> Option<&'s Spanned<MetaValue<'s>>>
1828 where
1829 'k: 's,
1830 {
1831 match self.key_values.keys().next() {
1834 None => None,
1835 Some(arbitrary_spanned_key) => {
1836 let key = spanned(key, *arbitrary_spanned_key.span());
1837 self.key_values.get(&key)
1838 }
1839 }
1840 }
1841
1842 pub fn tags(&self) -> impl ExactSizeIterator<Item = &Spanned<Tag<'a>>> {
1844 self.tags.iter()
1845 }
1846
1847 pub fn links(&self) -> impl ExactSizeIterator<Item = &Spanned<Link<'a>>> {
1849 self.links.iter()
1850 }
1851
1852 pub(crate) fn fmt_tags_links_inline(&self, f: &mut Formatter<'_>) -> fmt::Result {
1853 format(f, &self.tags, plain, SPACE, Some(SPACE))?;
1854 format(f, &self.links, plain, SPACE, Some(SPACE))
1855 }
1856
1857 pub(crate) fn fmt_tags_links_on_separate_lines(&self, f: &mut Formatter<'_>) -> fmt::Result {
1858 format(f, &self.tags, plain, NEWLINE_INDENT, Some(NEWLINE_INDENT))?;
1859 format(f, &self.links, plain, NEWLINE_INDENT, Some(NEWLINE_INDENT))
1860 }
1861
1862 pub(crate) fn fmt_keys_values(&self, f: &mut Formatter<'_>) -> fmt::Result {
1863 format(
1864 f,
1865 &self.key_values,
1866 key_value,
1867 NEWLINE_INDENT,
1868 Some(NEWLINE_INDENT),
1869 )
1870 }
1871}
1872
1873impl Display for Metadata<'_> {
1874 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1875 format(f, &self.tags, plain, NEWLINE_INDENT, Some(NEWLINE_INDENT))?;
1876 format(f, &self.links, plain, NEWLINE_INDENT, Some(NEWLINE_INDENT))?;
1877 self.fmt_keys_values(f)
1878 }
1879}
1880
1881#[derive(PartialEq, Eq, Clone, Debug)]
1882pub(crate) struct MetaKeyValue<'a> {
1883 pub(crate) key: Spanned<Key<'a>>,
1884 pub(crate) value: Spanned<MetaValue<'a>>,
1885}
1886
1887impl Display for MetaKeyValue<'_> {
1888 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1889 write!(f, "{}: \"{}\"", &self.key, &self.value)
1890 }
1891}
1892
1893#[derive(PartialEq, Eq, Clone, Debug)]
1895pub enum MetaValue<'a> {
1896 Simple(SimpleValue<'a>),
1897 Amount(Amount<'a>),
1898}
1899
1900impl Display for MetaValue<'_> {
1901 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1902 use MetaValue::*;
1903
1904 match self {
1905 Simple(simple_value) => simple_value.fmt(f),
1906 Amount(amount) => amount.fmt(f),
1907 }
1908 }
1909}
1910
1911#[derive(PartialEq, Eq, Clone, Debug)]
1913pub enum SimpleValue<'a> {
1914 String(&'a str),
1915 Currency(Currency<'a>),
1916 Account(Account<'a>),
1917 Tag(Tag<'a>),
1918 Link(Link<'a>),
1919 Date(Date),
1920 Bool(bool),
1921 Null,
1922 Expr(ExprValue),
1923}
1924
1925impl ElementType<'static> for SimpleValue<'_> {
1926 fn element_type(&self) -> &'static str {
1927 "simple value"
1928 }
1929}
1930
1931impl Display for SimpleValue<'_> {
1932 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1933 use SimpleValue::*;
1934
1935 match self {
1936 String(x) => write!(f, r#""{}""#, x),
1937 Currency(x) => x.fmt(f),
1938 Account(x) => x.fmt(f),
1939 Tag(x) => x.fmt(f),
1940 Link(x) => x.fmt(f),
1941 Date(x) => x.fmt(f),
1942 Bool(x) => f.write_str(if *x { "TRUE" } else { "FALSE" }),
1943 Null => f.write_str("NULL"),
1944 Expr(x) => x.fmt(f),
1945 }
1946 }
1947}
1948
1949#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
1951pub struct Tag<'a>(pub(crate) &'a str);
1952
1953impl<'a> TryFrom<&'a str> for Tag<'a> {
1954 type Error = TagOrLinkIdentifierError;
1955
1956 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1957 validate_tag_or_link_identifier(s)?;
1958 Ok(Tag(s))
1959 }
1960}
1961
1962impl ElementType<'static> for Tag<'_> {
1963 fn element_type(&self) -> &'static str {
1964 "tag"
1965 }
1966}
1967
1968impl<'a> AsRef<str> for Tag<'a> {
1969 fn as_ref(&self) -> &'a str {
1970 self.0
1971 }
1972}
1973
1974impl<'a> From<Tag<'a>> for &'a str {
1975 fn from(value: Tag<'a>) -> Self {
1976 value.0
1977 }
1978}
1979
1980impl<'a> From<&'_ Tag<'a>> for &'a str {
1981 fn from(value: &Tag<'a>) -> Self {
1982 value.0
1983 }
1984}
1985
1986impl PartialEq<&str> for Tag<'_> {
1987 fn eq(&self, other: &&str) -> bool {
1988 self.0 == *other
1989 }
1990}
1991
1992impl Display for Tag<'_> {
1993 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1994 write!(f, "#{}", self.0)
1995 }
1996}
1997
1998#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
2000pub struct TagOrLinkIdentifier<'a>(&'a str);
2001
2002#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
2003pub struct Link<'a>(pub(crate) &'a str);
2004
2005impl<'a> TryFrom<&'a str> for Link<'a> {
2006 type Error = TagOrLinkIdentifierError;
2007
2008 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
2009 validate_tag_or_link_identifier(s)?;
2010 Ok(Link(s))
2011 }
2012}
2013
2014impl ElementType<'static> for Link<'_> {
2015 fn element_type(&self) -> &'static str {
2016 "link"
2017 }
2018}
2019
2020impl PartialEq<&str> for Link<'_> {
2021 fn eq(&self, other: &&str) -> bool {
2022 self.0 == *other
2023 }
2024}
2025
2026impl Display for Link<'_> {
2027 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2028 write!(f, "^{}", self.0)
2029 }
2030}
2031
2032impl<'a> AsRef<str> for Link<'a> {
2033 fn as_ref(&self) -> &'a str {
2034 self.0
2035 }
2036}
2037
2038impl<'a> From<Link<'a>> for &'a str {
2039 fn from(value: Link<'a>) -> Self {
2040 value.0
2041 }
2042}
2043
2044impl<'a> From<&'_ Link<'a>> for &'a str {
2045 fn from(value: &Link<'a>) -> Self {
2046 value.0
2047 }
2048}
2049
2050const TAG_OR_LINK_EXTRA_CHARS: [char; 4] = ['-', '_', '/', '.'];
2052
2053fn is_valid_tag_or_link_identifier_char(c: &char) -> bool {
2054 c.is_alphanumeric() || TAG_OR_LINK_EXTRA_CHARS.contains(c)
2055}
2056
2057#[derive(PartialEq, Eq, Debug)]
2059pub struct TagOrLinkIdentifierError(Vec<char>);
2060
2061impl Display for TagOrLinkIdentifierError {
2062 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2063 format(f, &self.0, single_quoted, ", ", Some("invalid characters "))?;
2064 format(
2065 f,
2066 TAG_OR_LINK_EXTRA_CHARS,
2067 single_quoted,
2068 ", ",
2069 Some(" for tag or link identifier - must be alphanumeric or one of "),
2070 )
2071 }
2072}
2073
2074impl std::error::Error for TagOrLinkIdentifierError {}
2075
2076fn validate_tag_or_link_identifier(s: &str) -> Result<(), TagOrLinkIdentifierError> {
2077 let bad_chars = s
2078 .chars()
2079 .filter(|c| !is_valid_tag_or_link_identifier_char(c))
2080 .collect::<Vec<char>>();
2081 if bad_chars.is_empty() {
2082 Ok(())
2083 } else {
2084 Err(TagOrLinkIdentifierError(bad_chars))
2085 }
2086}
2087
2088#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
2090pub struct Key<'a>(&'a str);
2091
2092impl Key<'_> {
2093 pub(crate) fn is_valid_initial(c: &char) -> bool {
2094 c.is_ascii_lowercase()
2095 }
2096
2097 pub(crate) fn is_valid_subsequent(c: &char) -> bool {
2098 c.is_alphanumeric() || *c == '-' || *c == '_'
2099 }
2100}
2101
2102impl<'a> AsRef<str> for Key<'a> {
2103 fn as_ref(&self) -> &'a str {
2104 self.0
2105 }
2106}
2107
2108impl<'a> From<Key<'a>> for &'a str {
2109 fn from(value: Key<'a>) -> Self {
2110 value.0
2111 }
2112}
2113
2114impl<'a> From<&'_ Key<'a>> for &'a str {
2115 fn from(value: &Key<'a>) -> Self {
2116 value.0
2117 }
2118}
2119
2120impl ElementType<'static> for Key<'_> {
2121 fn element_type(&self) -> &'static str {
2122 "key"
2123 }
2124}
2125
2126impl PartialEq<&str> for Key<'_> {
2127 fn eq(&self, other: &&str) -> bool {
2128 self.0 == *other
2129 }
2130}
2131
2132impl Display for Key<'_> {
2133 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2134 write!(f, "{}", &self.0)
2135 }
2136}
2137
2138#[derive(PartialEq, Eq, Debug)]
2140pub struct KeyError(KeyErrorKind);
2141
2142#[derive(PartialEq, Eq, Debug)]
2143enum KeyErrorKind {
2144 Empty,
2145 Initial(char),
2146 Subsequent(Vec<char>),
2147}
2148
2149impl Display for KeyError {
2150 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2151 use KeyErrorKind::*;
2152 match &self.0 {
2153 Empty => write!(f, "empty key"),
2154 Initial(bad_char) => write!(
2155 f,
2156 "invalid character '{}' for key initial - must be lowercase ASCII letter",
2157 bad_char
2158 ),
2159 Subsequent(bad_chars) => {
2160 format(
2161 f,
2162 bad_chars,
2163 single_quoted,
2164 ", ",
2165 Some("invalid characters "),
2166 )?;
2167 f.write_str(" for key - must be alphanumeric or '-' or '_'")
2168 }
2169 }
2170 }
2171}
2172
2173impl std::error::Error for KeyError {}
2174
2175impl<'a> TryFrom<&'a str> for Key<'a> {
2176 type Error = KeyError;
2177
2178 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
2179 use KeyErrorKind::*;
2180 if s.is_empty() {
2181 Err(KeyError(Empty))
2182 } else {
2183 let mut chars = s.chars();
2184 let initial = chars.next().unwrap();
2185 if !Key::is_valid_initial(&initial) {
2186 Err(KeyError(Initial(initial)))
2187 } else {
2188 let bad_chars = chars
2189 .filter(|c| !Key::is_valid_subsequent(c))
2190 .collect::<Vec<char>>();
2191 if bad_chars.is_empty() {
2192 Ok(Key(s))
2193 } else {
2194 Err(KeyError(Subsequent(bad_chars)))
2195 }
2196 }
2197 }
2198 }
2199}
2200
2201#[derive(PartialEq, Eq, Clone, Debug)]
2202pub struct ExprValue {
2206 value: Decimal,
2207 expr: Expr,
2208}
2209
2210impl ExprValue {
2211 pub fn value(&self) -> Decimal {
2213 self.value
2214 }
2215
2216 pub fn expr(&self) -> &Expr {
2218 &self.expr
2219 }
2220}
2221
2222impl PartialEq<Decimal> for ExprValue {
2223 fn eq(&self, other: &Decimal) -> bool {
2224 &self.value == other
2225 }
2226}
2227
2228impl From<Expr> for ExprValue {
2229 fn from(expr: Expr) -> Self {
2231 let (mut value, scale) = expr.evaluate();
2232 value.rescale(scale);
2233 Self { value, expr }
2234 }
2235}
2236
2237impl ElementType<'static> for ExprValue {
2239 fn element_type(&self) -> &'static str {
2240 "amount" }
2242}
2243
2244impl Display for ExprValue {
2245 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2246 Display::fmt(&self.expr, format)
2247 }
2248}
2249
2250#[derive(PartialEq, Eq, Clone)]
2252pub enum Expr {
2253 Value(Decimal),
2254 Add(Box<Expr>, Box<Expr>),
2255 Sub(Box<Expr>, Box<Expr>),
2256 Mul(Box<Expr>, Box<Expr>),
2257 Div(Box<Expr>, Box<Expr>),
2258 Neg(Box<Expr>),
2259 Paren(Box<Expr>),
2260}
2261
2262impl Expr {
2263 fn evaluate(&self) -> (Decimal, u32) {
2264 fn evaluate_unary<F>(op: F, e1: &Expr) -> (Decimal, u32)
2265 where
2266 F: Fn(Decimal) -> Decimal,
2267 {
2268 let (d1, s1) = e1.evaluate();
2269 (op(d1), s1)
2270 }
2271
2272 fn evaluate_binary<F>(op: F, e1: &Expr, e2: &Expr) -> (Decimal, u32)
2273 where
2274 F: Fn(Decimal, Decimal) -> Decimal,
2275 {
2276 let (d1, s1) = e1.evaluate();
2277 let (d2, s2) = e2.evaluate();
2278 (op(d1, d2), max(s1, s2))
2279 }
2280
2281 use Expr::*;
2282 match self {
2283 Value(d) => (*d, d.scale()),
2284 Add(e1, e2) => evaluate_binary(std::ops::Add::add, e1, e2),
2285 Sub(e1, e2) => evaluate_binary(std::ops::Sub::sub, e1, e2),
2286 Mul(e1, e2) => evaluate_binary(std::ops::Mul::mul, e1, e2),
2287 Div(e1, e2) => evaluate_binary(std::ops::Div::div, e1, e2),
2288 Neg(e1) => evaluate_unary(std::ops::Neg::neg, e1),
2289 Paren(e) => e.evaluate(),
2290 }
2291 }
2292}
2293
2294impl Display for Expr {
2299 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2300 use self::Expr::*;
2301 match *self {
2302 Value(val) => write!(format, "{}", val),
2303 Add(ref left, ref right) => write!(format, "{} + {}", left, right),
2304 Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
2305 Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
2306 Div(ref left, ref right) => write!(format, "{} / {}", left, right),
2307 Neg(ref expr) => write!(format, "-{}", expr),
2308 Paren(ref expr) => write!(format, "({})", expr),
2309 }
2310 }
2311}
2312
2313impl fmt::Debug for Expr {
2314 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2315 use self::Expr::*;
2316 match *self {
2317 Value(val) => write!(format, "{}", val),
2318 Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
2319 Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
2320 Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
2321 Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
2322 Neg(ref expr) => write!(format, "(-{:?})", expr),
2323 Paren(ref expr) => write!(format, "[{:?}]", expr),
2324 }
2325 }
2326}
2327
2328#[derive(PartialEq, Eq, Clone, Debug)]
2330pub enum CompoundExprValue {
2331 PerUnit(ExprValue),
2332 Total(ExprValue),
2333 PerUnitAndTotal(ExprValue, ExprValue),
2334}
2335
2336impl Display for CompoundExprValue {
2337 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2338 use self::CompoundExprValue::*;
2339
2340 match self {
2341 PerUnit(e) => write!(format, "{}", e),
2342 Total(e) => write!(format, "# {}", e),
2343 PerUnitAndTotal(per_unit, total) => write!(format, "{} # {}", per_unit, total),
2344 }
2345 }
2346}
2347
2348#[derive(PartialEq, Eq, Clone, Debug)]
2350pub enum ScopedExprValue {
2351 PerUnit(ExprValue),
2352 Total(ExprValue),
2353}
2354
2355impl Display for ScopedExprValue {
2356 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2357 use self::ScopedExprValue::*;
2358
2359 match self {
2360 PerUnit(e) => write!(format, "{}", e),
2361 Total(e) => write!(format, "# {}", e),
2362 }
2363 }
2364}
2365
2366#[derive(PartialEq, Eq, Clone, Debug)]
2368pub struct Amount<'a> {
2369 number: Spanned<ExprValue>,
2370 currency: Spanned<Currency<'a>>,
2371}
2372
2373impl<'a> Amount<'a> {
2374 pub(crate) fn new(amount: (Spanned<ExprValue>, Spanned<Currency<'a>>)) -> Self {
2375 Amount {
2376 number: amount.0,
2377 currency: amount.1,
2378 }
2379 }
2380
2381 pub fn number(&self) -> &Spanned<ExprValue> {
2383 &self.number
2384 }
2385
2386 pub fn currency(&self) -> &Spanned<Currency<'a>> {
2388 &self.currency
2389 }
2390}
2391
2392impl Display for Amount<'_> {
2393 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2394 write!(format, "{} {}", &self.number, &self.currency)
2395 }
2396}
2397
2398#[derive(PartialEq, Eq, Clone, Debug)]
2400pub struct AmountWithTolerance<'a> {
2401 amount: Spanned<Amount<'a>>,
2402 tolerance: Option<Spanned<Decimal>>,
2403}
2404
2405impl<'a> AmountWithTolerance<'a> {
2406 pub(crate) fn new(awt: (Spanned<Amount<'a>>, Option<Spanned<Decimal>>)) -> Self {
2407 AmountWithTolerance {
2408 amount: awt.0,
2409 tolerance: awt.1,
2410 }
2411 }
2412
2413 pub fn amount(&self) -> &Spanned<Amount<'a>> {
2415 &self.amount
2416 }
2417
2418 pub fn tolerance(&self) -> Option<&Spanned<Decimal>> {
2420 self.tolerance.as_ref()
2421 }
2422}
2423
2424impl Display for AmountWithTolerance<'_> {
2425 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2426 if let Some(tolerance) = self.tolerance {
2427 write!(format, "{} ~ {}", &self.amount, tolerance)
2428 } else {
2429 write!(format, "{}", &self.amount)
2430 }
2431 }
2432}
2433
2434#[derive(PartialEq, Eq, Clone, Debug)]
2436pub struct LooseAmount<'a> {
2437 number: Option<Spanned<ExprValue>>,
2438 currency: Option<Spanned<Currency<'a>>>,
2439}
2440
2441impl<'a> LooseAmount<'a> {
2442 pub(crate) fn new(amount: (Option<Spanned<ExprValue>>, Option<Spanned<Currency<'a>>>)) -> Self {
2443 LooseAmount {
2444 number: amount.0,
2445 currency: amount.1,
2446 }
2447 }
2448
2449 pub fn number(&self) -> Option<&Spanned<ExprValue>> {
2451 self.number.as_ref()
2452 }
2453
2454 pub fn currency(&self) -> Option<&Spanned<Currency<'a>>> {
2456 self.currency.as_ref()
2457 }
2458}
2459
2460#[derive(PartialEq, Eq, Clone, Debug)]
2462pub enum CompoundAmount<'a> {
2463 BareCurrency(Currency<'a>),
2464 BareAmount(CompoundExprValue),
2465 CurrencyAmount(CompoundExprValue, Currency<'a>),
2466}
2467
2468impl Display for CompoundAmount<'_> {
2469 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2470 use self::CompoundAmount::*;
2471 match self {
2472 BareCurrency(cur) => write!(format, "{}", cur),
2473 BareAmount(ce) => write!(format, "{}", ce),
2474 CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2475 }
2476 }
2477}
2478
2479#[derive(PartialEq, Eq, Clone, Debug)]
2481pub struct CostSpec<'a> {
2482 per_unit: Option<Spanned<ExprValue>>,
2483 total: Option<Spanned<ExprValue>>,
2484 currency: Option<Spanned<Currency<'a>>>,
2485 date: Option<Spanned<Date>>,
2486 label: Option<Spanned<&'a str>>,
2487 merge: bool,
2488}
2489
2490impl<'a> CostSpec<'a> {
2491 pub fn per_unit(&self) -> Option<&Spanned<ExprValue>> {
2493 self.per_unit.as_ref()
2494 }
2495
2496 pub fn total(&self) -> Option<&Spanned<ExprValue>> {
2498 self.total.as_ref()
2499 }
2500
2501 pub fn currency(&self) -> Option<&Spanned<Currency<'a>>> {
2503 self.currency.as_ref()
2504 }
2505
2506 pub fn date(&self) -> Option<&Spanned<Date>> {
2508 self.date.as_ref()
2509 }
2510
2511 pub fn label(&self) -> Option<&Spanned<&'a str>> {
2513 self.label.as_ref()
2514 }
2515
2516 pub fn merge(&self) -> bool {
2518 self.merge
2519 }
2520}
2521
2522impl ElementType<'static> for CostSpec<'_> {
2523 fn element_type(&self) -> &'static str {
2524 "cost specification"
2525 }
2526}
2527
2528impl Display for CostSpec<'_> {
2529 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2530 let mut sep = "";
2531 let space = " ";
2532 let comma_space = ", ";
2533
2534 f.write_str("{")?;
2535
2536 if let Some(per_unit) = &self.per_unit {
2537 write!(f, "{}{}", sep, per_unit)?;
2538 sep = space;
2539 }
2540
2541 if let Some(total) = &self.total {
2542 write!(f, "{}# {}", sep, total)?;
2543 sep = space;
2544 }
2545
2546 if let Some(currency) = &self.currency {
2547 write!(f, "{}{}", sep, currency)?;
2548 sep = space;
2549 }
2550
2551 if sep == space {
2552 sep = comma_space
2553 }
2554
2555 if let Some(date) = &self.date {
2556 write!(f, "{}{}", sep, date)?;
2557 sep = comma_space;
2558 }
2559
2560 if let Some(label) = &self.label {
2561 write!(f, "{}\"{}\"", sep, label)?;
2562 sep = comma_space;
2563 }
2564
2565 if self.merge {
2566 write!(f, "{}*", sep)?;
2567 }
2568
2569 f.write_str("}")
2570 }
2571}
2572
2573#[derive(Default, Debug)]
2574pub(crate) struct CostSpecBuilder<'a> {
2576 per_unit: Option<Spanned<ExprValue>>,
2577 total: Option<Spanned<ExprValue>>,
2578 currency: Option<Spanned<Currency<'a>>>,
2579 date: Option<Spanned<Date>>,
2580 label: Option<Spanned<&'a str>>,
2581 merge: bool,
2582 errors: Vec<CostSpecError>,
2583}
2584
2585impl<'a> CostSpecBuilder<'a> {
2586 pub(crate) fn compound_expr(self, value: CompoundExprValue, span: Span) -> Self {
2587 use CompoundExprValue::*;
2588
2589 match value {
2590 PerUnit(value) => self.per_unit(spanned(value, span)),
2591 Total(value) => self.total(spanned(value, span)),
2592 PerUnitAndTotal(per_unit, total) => self
2593 .per_unit(spanned(per_unit, span))
2594 .total(spanned(total, span)),
2595 }
2596 }
2597
2598 fn per_unit(mut self, value: Spanned<ExprValue>) -> Self {
2599 if self.per_unit.is_none() {
2600 self.per_unit = Some(value);
2601 } else {
2602 self.errors.push(CostSpecError(CostSpecErrorKind::PerUnit))
2603 }
2604 self
2605 }
2606
2607 fn total(mut self, value: Spanned<ExprValue>) -> Self {
2608 if self.total.is_none() {
2609 self.total = Some(value);
2610 } else {
2611 self.errors.push(CostSpecError(CostSpecErrorKind::Total))
2612 }
2613 self
2614 }
2615
2616 pub(crate) fn currency(mut self, value: Currency<'a>, span: Span) -> Self {
2617 if self.currency.is_none() {
2618 self.currency = Some(spanned(value, span));
2619 } else {
2620 self.errors.push(CostSpecError(CostSpecErrorKind::Currency))
2621 }
2622 self
2623 }
2624
2625 pub(crate) fn date(mut self, value: Date, span: Span) -> Self {
2626 if self.date.is_none() {
2627 self.date = Some(spanned(value, span));
2628 } else {
2629 self.errors.push(CostSpecError(CostSpecErrorKind::Date))
2630 }
2631 self
2632 }
2633
2634 pub(crate) fn label(mut self, value: &'a str, span: Span) -> Self {
2635 if self.label.is_none() {
2636 self.label = Some(spanned(value, span));
2637 } else {
2638 self.errors.push(CostSpecError(CostSpecErrorKind::Label))
2639 }
2640 self
2641 }
2642
2643 pub(crate) fn merge(mut self, _span: Span) -> Self {
2644 if !self.merge {
2645 self.merge = true;
2647 } else {
2648 self.errors
2649 .push(CostSpecError(CostSpecErrorKind::MergeCost))
2650 }
2651 self
2652 }
2653
2654 pub(crate) fn build<'b>(&'b mut self) -> Result<CostSpec<'a>, CostSpecErrors>
2656 where
2657 'a: 'b,
2658 {
2659 let per_unit = self.per_unit.take();
2660 let total = self.total.take();
2661 let currency = self.currency.take();
2662 let date = self.date.take();
2663 let label = self.label.take();
2664 let merge = self.merge;
2665 self.merge = false;
2666
2667 if !self.errors.is_empty() {
2668 let mut errors = Vec::new();
2669 swap(&mut self.errors, &mut errors);
2670 Err(CostSpecErrors(errors))
2671 } else {
2672 Ok(CostSpec {
2673 per_unit,
2674 total,
2675 currency,
2676 date,
2677 label,
2678 merge,
2679 })
2680 }
2681 }
2683}
2684
2685#[derive(PartialEq, Eq, Debug)]
2687pub struct CostSpecError(CostSpecErrorKind);
2688
2689#[derive(PartialEq, Eq, Display, Debug)]
2690#[strum(serialize_all = "kebab-case")]
2691enum CostSpecErrorKind {
2692 PerUnit,
2693 Total,
2694 Currency,
2695 Date,
2696 Label,
2697 MergeCost,
2698}
2699
2700impl CostSpecErrorKind {
2701 fn unless(self, condition: bool) -> Result<(), CostSpecError> {
2702 if condition {
2703 Ok(())
2704 } else {
2705 Err(CostSpecError(self))
2706 }
2707 }
2708}
2709
2710impl Display for CostSpecError {
2711 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2712 write!(f, "duplicate {} field in cost specification", self.0)
2713 }
2714}
2715
2716impl std::error::Error for CostSpecError {}
2717
2718#[derive(PartialEq, Eq, Debug)]
2720pub struct CostSpecErrors(Vec<CostSpecError>);
2721
2722impl Display for CostSpecErrors {
2723 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2724 format(f, &self.0, plain, ", ", None)
2725 }
2726}
2727
2728impl std::error::Error for CostSpecErrors {}
2729
2730#[derive(PartialEq, Eq, Clone, Debug)]
2733pub enum PriceSpec<'a> {
2734 Unspecified,
2735 BareCurrency(Currency<'a>),
2736 BareAmount(ScopedExprValue),
2737 CurrencyAmount(ScopedExprValue, Currency<'a>),
2738}
2739
2740impl Display for PriceSpec<'_> {
2741 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2742 use self::PriceSpec::*;
2743 match self {
2744 Unspecified => Ok(()),
2745 BareCurrency(cur) => write!(format, "{}", cur),
2746 BareAmount(ce) => write!(format, "{}", ce),
2747 CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2748 }
2749 }
2750}
2751
2752#[derive(Debug)]
2753pub(crate) struct AccountTypeNames<'a> {
2754 pub(crate) name_by_type: Vec<AccountTypeName<'a>>,
2756 pub(crate) type_by_name: HashMap<AccountTypeName<'a>, AccountType>,
2757}
2758
2759impl<'a> AccountTypeNames<'a> {
2760 pub(crate) fn name(&self, account_type: AccountType) -> AccountTypeName<'a> {
2761 self.name_by_type[account_type as usize]
2762 }
2763
2764 pub(crate) fn get(&self, name: &AccountTypeName) -> Option<AccountType> {
2765 self.type_by_name.get(name).copied()
2766 }
2767
2768 pub(crate) fn update(
2769 &mut self,
2770 account_type: AccountType,
2771 name: AccountTypeName<'a>,
2772 ) -> Result<(), AccountTypeNamesError> {
2773 use hash_map::Entry::*;
2774
2775 match self.type_by_name.entry(name) {
2776 Vacant(e) => {
2777 e.insert(account_type);
2778 let old_name = self.name_by_type[account_type as usize];
2779 self.name_by_type[account_type as usize] = name;
2780 self.type_by_name.remove(&old_name);
2781 Ok(())
2782 }
2783 Occupied(o) => {
2784 let existing_account_type = *o.get();
2785 if existing_account_type == account_type {
2786 Ok(())
2788 } else {
2789 Err(AccountTypeNamesError(AccountTypeNamesErrorKind::NameInUse(
2790 existing_account_type,
2791 )))
2792 }
2793 }
2794 }
2795 }
2796}
2797
2798impl<'a> Default for AccountTypeNames<'a> {
2799 fn default() -> Self {
2800 use AccountType::*;
2801
2802 let names_types = vec![
2803 ("Assets", Assets),
2804 ("Liabilities", Liabilities),
2805 ("Equity", Equity),
2806 ("Income", Income),
2807 ("Expenses", Expenses),
2808 ];
2809
2810 let mut names_type_indices = names_types
2811 .iter()
2812 .map(|(n, t)| (*n, *t as usize))
2813 .collect::<Vec<_>>();
2814 names_type_indices.sort_by_key(|(_n, t)| *t);
2815
2816 let name_by_type = names_type_indices
2817 .into_iter()
2818 .map(|(n, _t)| AccountTypeName::try_from(n).unwrap())
2819 .collect::<Vec<_>>();
2820
2821 let type_by_name: HashMap<AccountTypeName<'a>, AccountType> = HashMap::from_iter(
2822 names_types
2823 .into_iter()
2824 .map(|(n, t)| (AccountTypeName::try_from(n).unwrap(), t)),
2825 );
2826
2827 AccountTypeNames {
2828 name_by_type,
2829 type_by_name,
2830 }
2831 }
2832}
2833
2834impl Display for AccountTypeNames<'_> {
2835 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2836 format(f, &self.name_by_type, plain, ", ", None)
2837 }
2838}
2839
2840#[derive(PartialEq, Eq, Debug)]
2841pub(crate) struct AccountTypeNamesError(AccountTypeNamesErrorKind);
2842
2843#[derive(PartialEq, Eq, Debug)]
2844enum AccountTypeNamesErrorKind {
2845 NameInUse(AccountType),
2846}
2847
2848impl Display for AccountTypeNamesError {
2849 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2850 use AccountTypeNamesErrorKind::*;
2851 match &self.0 {
2852 NameInUse(t) => write!(f, "account type name in use for {}", t.as_ref()),
2853 }
2854 }
2855}
2856
2857impl std::error::Error for AccountTypeNamesError {}
2858
2859mod tests;