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