beancount_parser_lima/
types.rs

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