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<'a> AsRef<str> for Account<'a> {
1159    fn as_ref(&self) -> &'a 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<'a> AsRef<str> for Subaccount<'a> {
1201    fn as_ref(&self) -> &'a 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<'a> AsRef<str> for AccountTypeName<'a> {
1277    fn as_ref(&self) -> &'a 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<'a> AsRef<str> for AccountName<'a> {
1352    fn as_ref(&self) -> &'a 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<'a> AsRef<str> for Currency<'a> {
1456    fn as_ref(&self) -> &'a 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 without the `#` prefix.
1778#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
1779pub struct Tag<'a>(pub(crate) &'a str);
1780
1781impl<'a> TryFrom<&'a str> for Tag<'a> {
1782    type Error = TagOrLinkIdentifierError;
1783
1784    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1785        validate_tag_or_link_identifier(s)?;
1786        Ok(Tag(s))
1787    }
1788}
1789
1790impl ElementType for Tag<'_> {
1791    fn element_type(&self) -> &'static str {
1792        "tag"
1793    }
1794}
1795
1796impl<'a> AsRef<str> for Tag<'a> {
1797    fn as_ref(&self) -> &'a str {
1798        self.0
1799    }
1800}
1801
1802impl PartialEq<&str> for Tag<'_> {
1803    fn eq(&self, other: &&str) -> bool {
1804        self.0 == *other
1805    }
1806}
1807
1808impl Display for Tag<'_> {
1809    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1810        write!(f, "#{}", self.0)
1811    }
1812}
1813
1814/// A link without the `^` prefix.
1815#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1816pub struct TagOrLinkIdentifier<'a>(&'a str);
1817
1818#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
1819pub struct Link<'a>(pub(crate) &'a str);
1820
1821impl<'a> TryFrom<&'a str> for Link<'a> {
1822    type Error = TagOrLinkIdentifierError;
1823
1824    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1825        validate_tag_or_link_identifier(s)?;
1826        Ok(Link(s))
1827    }
1828}
1829
1830impl ElementType for Link<'_> {
1831    fn element_type(&self) -> &'static str {
1832        "link"
1833    }
1834}
1835
1836impl PartialEq<&str> for Link<'_> {
1837    fn eq(&self, other: &&str) -> bool {
1838        self.0 == *other
1839    }
1840}
1841
1842impl Display for Link<'_> {
1843    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1844        write!(f, "^{}", self.0)
1845    }
1846}
1847
1848impl<'a> AsRef<str> for Link<'a> {
1849    fn as_ref(&self) -> &'a str {
1850        self.0
1851    }
1852}
1853
1854/// The valid characters for tags and links besides alphanumeric.
1855const TAG_OR_LINK_EXTRA_CHARS: [char; 4] = ['-', '_', '/', '.'];
1856
1857fn is_valid_tag_or_link_identifier_char(c: &char) -> bool {
1858    c.is_alphanumeric() || TAG_OR_LINK_EXTRA_CHARS.contains(c)
1859}
1860
1861/// Error type for [TagOrLinkIdentifier] creation.
1862#[derive(PartialEq, Eq, Debug)]
1863pub struct TagOrLinkIdentifierError(Vec<char>);
1864
1865impl Display for TagOrLinkIdentifierError {
1866    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1867        format(f, &self.0, single_quoted, ", ", Some("invalid characters "))?;
1868        format(
1869            f,
1870            TAG_OR_LINK_EXTRA_CHARS,
1871            single_quoted,
1872            ", ",
1873            Some(" for tag or link identifier - must be alphanumeric or one of "),
1874        )
1875    }
1876}
1877
1878impl std::error::Error for TagOrLinkIdentifierError {}
1879
1880fn validate_tag_or_link_identifier(s: &str) -> Result<(), TagOrLinkIdentifierError> {
1881    let bad_chars = s
1882        .chars()
1883        .filter(|c| !is_valid_tag_or_link_identifier_char(c))
1884        .collect::<Vec<char>>();
1885    if bad_chars.is_empty() {
1886        Ok(())
1887    } else {
1888        Err(TagOrLinkIdentifierError(bad_chars))
1889    }
1890}
1891
1892/// A key for a [Metadata] [MetaValue].
1893#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1894pub struct Key<'a>(&'a str);
1895
1896impl Key<'_> {
1897    pub(crate) fn is_valid_initial(c: &char) -> bool {
1898        c.is_ascii_lowercase()
1899    }
1900
1901    pub(crate) fn is_valid_subsequent(c: &char) -> bool {
1902        c.is_alphanumeric() || *c == '-' || *c == '_'
1903    }
1904}
1905
1906impl<'a> AsRef<str> for Key<'a> {
1907    fn as_ref(&self) -> &'a str {
1908        self.0
1909    }
1910}
1911
1912impl ElementType for Key<'_> {
1913    fn element_type(&self) -> &'static str {
1914        "key"
1915    }
1916}
1917
1918impl PartialEq<&str> for Key<'_> {
1919    fn eq(&self, other: &&str) -> bool {
1920        self.0 == *other
1921    }
1922}
1923
1924impl Display for Key<'_> {
1925    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1926        write!(f, "{}", &self.0)
1927    }
1928}
1929
1930/// Error type for [Key] creation.
1931#[derive(PartialEq, Eq, Debug)]
1932pub struct KeyError(KeyErrorKind);
1933
1934#[derive(PartialEq, Eq, Debug)]
1935enum KeyErrorKind {
1936    Empty,
1937    Initial(char),
1938    Subsequent(Vec<char>),
1939}
1940
1941impl Display for KeyError {
1942    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1943        use KeyErrorKind::*;
1944        match &self.0 {
1945            Empty => write!(f, "empty key"),
1946            Initial(bad_char) => write!(
1947                f,
1948                "invalid character '{}' for key initial - must be lowercase ASCII letter",
1949                bad_char
1950            ),
1951            Subsequent(bad_chars) => {
1952                format(
1953                    f,
1954                    bad_chars,
1955                    single_quoted,
1956                    ", ",
1957                    Some("invalid characters "),
1958                )?;
1959                f.write_str(" for key - must be alphanumeric or '-' or '_'")
1960            }
1961        }
1962    }
1963}
1964
1965impl std::error::Error for KeyError {}
1966
1967impl<'a> TryFrom<&'a str> for Key<'a> {
1968    type Error = KeyError;
1969
1970    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1971        use KeyErrorKind::*;
1972        if s.is_empty() {
1973            Err(KeyError(Empty))
1974        } else {
1975            let mut chars = s.chars();
1976            let initial = chars.next().unwrap();
1977            if !Key::is_valid_initial(&initial) {
1978                Err(KeyError(Initial(initial)))
1979            } else {
1980                let bad_chars = chars
1981                    .filter(|c| !Key::is_valid_subsequent(c))
1982                    .collect::<Vec<char>>();
1983                if bad_chars.is_empty() {
1984                    Ok(Key(s))
1985                } else {
1986                    Err(KeyError(Subsequent(bad_chars)))
1987                }
1988            }
1989        }
1990    }
1991}
1992
1993#[derive(PartialEq, Eq, Clone, Debug)]
1994/// An `Expr` which has been evaluated.
1995///
1996/// 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.
1997pub struct ExprValue {
1998    value: Decimal,
1999    expr: Expr,
2000}
2001
2002impl ExprValue {
2003    /// Field accessor.
2004    pub fn value(&self) -> Decimal {
2005        self.value
2006    }
2007
2008    /// Field accessor.
2009    pub fn expr(&self) -> &Expr {
2010        &self.expr
2011    }
2012}
2013
2014impl PartialEq<Decimal> for ExprValue {
2015    fn eq(&self, other: &Decimal) -> bool {
2016        &self.value == other
2017    }
2018}
2019
2020impl From<Expr> for ExprValue {
2021    /// Evaluate the `Expr` rounding to the max scale it contains.
2022    fn from(expr: Expr) -> Self {
2023        let (mut value, scale) = expr.evaluate();
2024        value.rescale(scale);
2025        Self { value, expr }
2026    }
2027}
2028
2029//
2030impl ElementType for ExprValue {
2031    fn element_type(&self) -> &'static str {
2032        "amount" // is there a better user-facing name?
2033    }
2034}
2035
2036impl Display for ExprValue {
2037    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2038        Display::fmt(&self.expr, format)
2039    }
2040}
2041
2042/// A numeric expression which respects standard operator precedence.
2043#[derive(PartialEq, Eq, Clone)]
2044pub enum Expr {
2045    Value(Decimal),
2046    Add(Box<Expr>, Box<Expr>),
2047    Sub(Box<Expr>, Box<Expr>),
2048    Mul(Box<Expr>, Box<Expr>),
2049    Div(Box<Expr>, Box<Expr>),
2050    Neg(Box<Expr>),
2051    Paren(Box<Expr>),
2052}
2053
2054impl Expr {
2055    fn evaluate(&self) -> (Decimal, u32) {
2056        fn evaluate_unary<F>(op: F, e1: &Expr) -> (Decimal, u32)
2057        where
2058            F: Fn(Decimal) -> Decimal,
2059        {
2060            let (d1, s1) = e1.evaluate();
2061            (op(d1), s1)
2062        }
2063
2064        fn evaluate_binary<F>(op: F, e1: &Expr, e2: &Expr) -> (Decimal, u32)
2065        where
2066            F: Fn(Decimal, Decimal) -> Decimal,
2067        {
2068            let (d1, s1) = e1.evaluate();
2069            let (d2, s2) = e2.evaluate();
2070            (op(d1, d2), max(s1, s2))
2071        }
2072
2073        use Expr::*;
2074        match self {
2075            Value(d) => (*d, d.scale()),
2076            Add(e1, e2) => evaluate_binary(std::ops::Add::add, e1, e2),
2077            Sub(e1, e2) => evaluate_binary(std::ops::Sub::sub, e1, e2),
2078            Mul(e1, e2) => evaluate_binary(std::ops::Mul::mul, e1, e2),
2079            Div(e1, e2) => evaluate_binary(std::ops::Div::div, e1, e2),
2080            Neg(e1) => evaluate_unary(std::ops::Neg::neg, e1),
2081            Paren(e) => e.evaluate(),
2082        }
2083    }
2084}
2085
2086// impl PartialEq for Expr {
2087
2088// }
2089
2090impl Display for Expr {
2091    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2092        use self::Expr::*;
2093        match *self {
2094            Value(val) => write!(format, "{}", val),
2095            Add(ref left, ref right) => write!(format, "{} + {}", left, right),
2096            Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
2097            Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
2098            Div(ref left, ref right) => write!(format, "{} / {}", left, right),
2099            Neg(ref expr) => write!(format, "-{}", expr),
2100            Paren(ref expr) => write!(format, "({})", expr),
2101        }
2102    }
2103}
2104
2105impl fmt::Debug for Expr {
2106    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2107        use self::Expr::*;
2108        match *self {
2109            Value(val) => write!(format, "{}", val),
2110            Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
2111            Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
2112            Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
2113            Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
2114            Neg(ref expr) => write!(format, "(-{:?})", expr),
2115            Paren(ref expr) => write!(format, "[{:?}]", expr),
2116        }
2117    }
2118}
2119
2120/// An `ExprValue` which quantifies a total or per-unit, or both.
2121#[derive(PartialEq, Eq, Clone, Debug)]
2122pub enum CompoundExprValue {
2123    PerUnit(ExprValue),
2124    Total(ExprValue),
2125    PerUnitAndTotal(ExprValue, ExprValue),
2126}
2127
2128impl Display for CompoundExprValue {
2129    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2130        use self::CompoundExprValue::*;
2131
2132        match self {
2133            PerUnit(e) => write!(format, "{}", e),
2134            Total(e) => write!(format, "# {}", e),
2135            PerUnitAndTotal(per_unit, total) => write!(format, "{} # {}", per_unit, total),
2136        }
2137    }
2138}
2139
2140/// An `ExprValue` which quantifies either a total or per-unit, but not both.
2141#[derive(PartialEq, Eq, Clone, Debug)]
2142pub enum ScopedExprValue {
2143    PerUnit(ExprValue),
2144    Total(ExprValue),
2145}
2146
2147impl Display for ScopedExprValue {
2148    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2149        use self::ScopedExprValue::*;
2150
2151        match self {
2152            PerUnit(e) => write!(format, "{}", e),
2153            Total(e) => write!(format, "# {}", e),
2154        }
2155    }
2156}
2157
2158/// A `ExprValue` and `Currency`.
2159#[derive(PartialEq, Eq, Clone, Debug)]
2160pub struct Amount<'a> {
2161    number: Spanned<ExprValue>,
2162    currency: Spanned<Currency<'a>>,
2163}
2164
2165impl<'a> Amount<'a> {
2166    pub(crate) fn new(amount: (Spanned<ExprValue>, Spanned<Currency<'a>>)) -> Self {
2167        Amount {
2168            number: amount.0,
2169            currency: amount.1,
2170        }
2171    }
2172
2173    /// Field accessor.
2174    pub fn number(&self) -> &Spanned<ExprValue> {
2175        &self.number
2176    }
2177
2178    /// Field accessor.
2179    pub fn currency(&self) -> &Spanned<Currency<'a>> {
2180        &self.currency
2181    }
2182}
2183
2184impl Display for Amount<'_> {
2185    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2186        write!(format, "{} {}", &self.number, &self.currency)
2187    }
2188}
2189
2190/// An `Amount` with optional tolerance.
2191#[derive(PartialEq, Eq, Clone, Debug)]
2192pub struct AmountWithTolerance<'a> {
2193    amount: Spanned<Amount<'a>>,
2194    tolerance: Option<Spanned<Decimal>>,
2195}
2196
2197impl<'a> AmountWithTolerance<'a> {
2198    pub(crate) fn new(awt: (Spanned<Amount<'a>>, Option<Spanned<Decimal>>)) -> Self {
2199        AmountWithTolerance {
2200            amount: awt.0,
2201            tolerance: awt.1,
2202        }
2203    }
2204
2205    /// Field accessor.
2206    pub fn amount(&self) -> &Spanned<Amount<'a>> {
2207        &self.amount
2208    }
2209
2210    /// Field accessor.
2211    pub fn tolerance(&self) -> Option<&Spanned<Decimal>> {
2212        self.tolerance.as_ref()
2213    }
2214}
2215
2216impl Display for AmountWithTolerance<'_> {
2217    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2218        if let Some(tolerance) = self.tolerance {
2219            write!(format, "{} ~ {}", &self.amount, tolerance)
2220        } else {
2221            write!(format, "{}", &self.amount)
2222        }
2223    }
2224}
2225
2226/// An amount where each element of `ExprValue` and `Currency` may not actually be specified.
2227#[derive(PartialEq, Eq, Clone, Debug)]
2228pub struct LooseAmount<'a> {
2229    number: Option<Spanned<ExprValue>>,
2230    currency: Option<Spanned<Currency<'a>>>,
2231}
2232
2233impl<'a> LooseAmount<'a> {
2234    pub(crate) fn new(amount: (Option<Spanned<ExprValue>>, Option<Spanned<Currency<'a>>>)) -> Self {
2235        LooseAmount {
2236            number: amount.0,
2237            currency: amount.1,
2238        }
2239    }
2240
2241    /// Field accessor.
2242    pub fn number(&self) -> Option<&Spanned<ExprValue>> {
2243        self.number.as_ref()
2244    }
2245
2246    /// Field accessor.
2247    pub fn currency(&self) -> Option<&Spanned<Currency<'a>>> {
2248        self.currency.as_ref()
2249    }
2250}
2251
2252/// An amount which specifies a total or per-unit value or both, with or without a currency, or simply just a `Currency`.
2253#[derive(PartialEq, Eq, Clone, Debug)]
2254pub enum CompoundAmount<'a> {
2255    BareCurrency(Currency<'a>),
2256    BareAmount(CompoundExprValue),
2257    CurrencyAmount(CompoundExprValue, Currency<'a>),
2258}
2259
2260impl Display for CompoundAmount<'_> {
2261    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2262        use self::CompoundAmount::*;
2263        match self {
2264            BareCurrency(cur) => write!(format, "{}", cur),
2265            BareAmount(ce) => write!(format, "{}", ce),
2266            CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2267        }
2268    }
2269}
2270
2271/// A cost specification.
2272#[derive(PartialEq, Eq, Clone, Debug)]
2273pub struct CostSpec<'a> {
2274    per_unit: Option<Spanned<ExprValue>>,
2275    total: Option<Spanned<ExprValue>>,
2276    currency: Option<Spanned<Currency<'a>>>,
2277    date: Option<Spanned<Date>>,
2278    label: Option<Spanned<&'a str>>,
2279    merge: bool,
2280}
2281
2282impl CostSpec<'_> {
2283    /// Field accessor.
2284    pub fn per_unit(&self) -> Option<&Spanned<ExprValue>> {
2285        self.per_unit.as_ref()
2286    }
2287
2288    /// Field accessor.
2289    pub fn total(&self) -> Option<&Spanned<ExprValue>> {
2290        self.total.as_ref()
2291    }
2292
2293    /// Field accessor.
2294    pub fn currency(&self) -> Option<&Spanned<Currency<'_>>> {
2295        self.currency.as_ref()
2296    }
2297
2298    /// Field accessor.
2299    pub fn date(&self) -> Option<&Spanned<Date>> {
2300        self.date.as_ref()
2301    }
2302
2303    /// Field accessor.
2304    pub fn label(&self) -> Option<&Spanned<&str>> {
2305        self.label.as_ref()
2306    }
2307
2308    /// Field accessor.
2309    pub fn merge(&self) -> bool {
2310        self.merge
2311    }
2312}
2313
2314impl ElementType for CostSpec<'_> {
2315    fn element_type(&self) -> &'static str {
2316        "cost specification"
2317    }
2318}
2319
2320impl Display for CostSpec<'_> {
2321    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2322        let mut prefix = "";
2323        let space = " ";
2324
2325        f.write_str("{")?;
2326
2327        if let Some(per_unit) = &self.per_unit {
2328            write!(f, "{}{}", prefix, per_unit)?;
2329            prefix = space;
2330        }
2331
2332        if let Some(total) = &self.total {
2333            write!(f, "{}# {}", prefix, total)?;
2334            prefix = space;
2335        }
2336
2337        if let Some(currency) = &self.currency {
2338            write!(f, "{}{}", prefix, currency)?;
2339            prefix = space;
2340        }
2341
2342        if let Some(date) = &self.date {
2343            write!(f, "{}{}", prefix, date)?;
2344            prefix = space;
2345        }
2346
2347        if let Some(label) = &self.label {
2348            write!(f, "{}\"{}\"", prefix, label)?;
2349            prefix = space;
2350        }
2351
2352        if self.merge {
2353            write!(f, "{}*", prefix)?;
2354        }
2355
2356        f.write_str("}")
2357    }
2358}
2359
2360#[derive(Default, Debug)]
2361/// Only allows setting each field once, and requires at least one field to be set before building.
2362pub(crate) struct CostSpecBuilder<'a> {
2363    per_unit: Option<Spanned<ExprValue>>,
2364    total: Option<Spanned<ExprValue>>,
2365    currency: Option<Spanned<Currency<'a>>>,
2366    date: Option<Spanned<Date>>,
2367    label: Option<Spanned<&'a str>>,
2368    merge: bool,
2369    errors: Vec<CostSpecError>,
2370}
2371
2372impl<'a> CostSpecBuilder<'a> {
2373    pub(crate) fn compound_expr(self, value: CompoundExprValue, span: Span) -> Self {
2374        use CompoundExprValue::*;
2375
2376        match value {
2377            PerUnit(value) => self.per_unit(spanned(value, span)),
2378            Total(value) => self.total(spanned(value, span)),
2379            PerUnitAndTotal(per_unit, total) => self
2380                .per_unit(spanned(per_unit, span))
2381                .total(spanned(total, span)),
2382        }
2383    }
2384
2385    fn per_unit(mut self, value: Spanned<ExprValue>) -> Self {
2386        if self.per_unit.is_none() {
2387            self.per_unit = Some(value);
2388        } else {
2389            self.errors.push(CostSpecError(CostSpecErrorKind::PerUnit))
2390        }
2391        self
2392    }
2393
2394    fn total(mut self, value: Spanned<ExprValue>) -> Self {
2395        if self.total.is_none() {
2396            self.total = Some(value);
2397        } else {
2398            self.errors.push(CostSpecError(CostSpecErrorKind::Total))
2399        }
2400        self
2401    }
2402
2403    pub(crate) fn currency(mut self, value: Currency<'a>, span: Span) -> Self {
2404        if self.currency.is_none() {
2405            self.currency = Some(spanned(value, span));
2406        } else {
2407            self.errors.push(CostSpecError(CostSpecErrorKind::Currency))
2408        }
2409        self
2410    }
2411
2412    pub(crate) fn date(mut self, value: Date, span: Span) -> Self {
2413        if self.date.is_none() {
2414            self.date = Some(spanned(value, span));
2415        } else {
2416            self.errors.push(CostSpecError(CostSpecErrorKind::Date))
2417        }
2418        self
2419    }
2420
2421    pub(crate) fn label(mut self, value: &'a str, span: Span) -> Self {
2422        if self.label.is_none() {
2423            self.label = Some(spanned(value, span));
2424        } else {
2425            self.errors.push(CostSpecError(CostSpecErrorKind::Label))
2426        }
2427        self
2428    }
2429
2430    pub(crate) fn merge(mut self, _span: Span) -> Self {
2431        if !self.merge {
2432            // TODO find a way to keep a span iff merge is true
2433            self.merge = true;
2434        } else {
2435            self.errors
2436                .push(CostSpecError(CostSpecErrorKind::MergeCost))
2437        }
2438        self
2439    }
2440
2441    // the lifetime `'a` of the CostSpec returned outlives the builder lifetime `'b`
2442    pub(crate) fn build<'b>(&'b mut self) -> Result<CostSpec<'a>, CostSpecErrors>
2443    where
2444        'a: 'b,
2445    {
2446        let per_unit = self.per_unit.take();
2447        let total = self.total.take();
2448        let currency = self.currency.take();
2449        let date = self.date.take();
2450        let label = self.label.take();
2451        let merge = self.merge;
2452        self.merge = false;
2453
2454        if !self.errors.is_empty() {
2455            let mut errors = Vec::new();
2456            swap(&mut self.errors, &mut errors);
2457            Err(CostSpecErrors(errors))
2458        } else {
2459            Ok(CostSpec {
2460                per_unit,
2461                total,
2462                currency,
2463                date,
2464                label,
2465                merge,
2466            })
2467        }
2468        // let errors: Vec<CostSpecError>,
2469    }
2470}
2471
2472/// Error type for [CostSpec] creation.
2473#[derive(PartialEq, Eq, Debug)]
2474pub struct CostSpecError(CostSpecErrorKind);
2475
2476#[derive(PartialEq, Eq, Display, Debug)]
2477#[strum(serialize_all = "kebab-case")]
2478enum CostSpecErrorKind {
2479    PerUnit,
2480    Total,
2481    Currency,
2482    Date,
2483    Label,
2484    MergeCost,
2485}
2486
2487impl CostSpecErrorKind {
2488    fn unless(self, condition: bool) -> Result<(), CostSpecError> {
2489        if condition {
2490            Ok(())
2491        } else {
2492            Err(CostSpecError(self))
2493        }
2494    }
2495}
2496
2497impl Display for CostSpecError {
2498    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2499        write!(f, "duplicate {} field in cost specification", self.0)
2500    }
2501}
2502
2503impl std::error::Error for CostSpecError {}
2504
2505/// Multiple errors arising from creation of a [CostSpec].
2506#[derive(PartialEq, Eq, Debug)]
2507pub struct CostSpecErrors(Vec<CostSpecError>);
2508
2509impl Display for CostSpecErrors {
2510    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2511        format(f, &self.0, plain, ", ", None)
2512    }
2513}
2514
2515impl std::error::Error for CostSpecErrors {}
2516
2517/// An amount which specifies a total or per-unit value, with or without a currency, or simply just a `Currency`.
2518/// Unlike a `CompoundAmount` it is forbidden to have both total and per-unit.
2519#[derive(PartialEq, Eq, Clone, Debug)]
2520pub enum PriceSpec<'a> {
2521    Unspecified,
2522    BareCurrency(Currency<'a>),
2523    BareAmount(ScopedExprValue),
2524    CurrencyAmount(ScopedExprValue, Currency<'a>),
2525}
2526
2527impl Display for PriceSpec<'_> {
2528    fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2529        use self::PriceSpec::*;
2530        match self {
2531            Unspecified => Ok(()),
2532            BareCurrency(cur) => write!(format, "{}", cur),
2533            BareAmount(ce) => write!(format, "{}", ce),
2534            CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2535        }
2536    }
2537}
2538
2539#[derive(Debug)]
2540pub(crate) struct AccountTypeNames<'a> {
2541    // relies on AccountType discriminants being contiguous and from zero, which they are
2542    pub(crate) name_by_type: Vec<AccountTypeName<'a>>,
2543    pub(crate) type_by_name: HashMap<AccountTypeName<'a>, AccountType>,
2544}
2545
2546impl<'a> AccountTypeNames<'a> {
2547    pub(crate) fn name(&self, account_type: AccountType) -> AccountTypeName<'a> {
2548        self.name_by_type[account_type as usize]
2549    }
2550
2551    pub(crate) fn get(&self, name: &AccountTypeName) -> Option<AccountType> {
2552        self.type_by_name.get(name).copied()
2553    }
2554
2555    pub(crate) fn update(
2556        &mut self,
2557        account_type: AccountType,
2558        name: AccountTypeName<'a>,
2559    ) -> Result<(), AccountTypeNamesError> {
2560        use hash_map::Entry::*;
2561
2562        match self.type_by_name.entry(name) {
2563            Vacant(e) => {
2564                e.insert(account_type);
2565                let old_name = self.name_by_type[account_type as usize];
2566                self.name_by_type[account_type as usize] = name;
2567                self.type_by_name.remove(&old_name);
2568                Ok(())
2569            }
2570            Occupied(o) => {
2571                let existing_account_type = *o.get();
2572                if existing_account_type == account_type {
2573                    // updating as same, harmless
2574                    Ok(())
2575                } else {
2576                    Err(AccountTypeNamesError(AccountTypeNamesErrorKind::NameInUse(
2577                        existing_account_type,
2578                    )))
2579                }
2580            }
2581        }
2582    }
2583}
2584
2585impl<'a> Default for AccountTypeNames<'a> {
2586    fn default() -> Self {
2587        use AccountType::*;
2588
2589        let names_types = vec![
2590            ("Assets", Assets),
2591            ("Liabilities", Liabilities),
2592            ("Equity", Equity),
2593            ("Income", Income),
2594            ("Expenses", Expenses),
2595        ];
2596
2597        let mut names_type_indices = names_types
2598            .iter()
2599            .map(|(n, t)| (*n, *t as usize))
2600            .collect::<Vec<_>>();
2601        names_type_indices.sort_by_key(|(_n, t)| *t);
2602
2603        let name_by_type = names_type_indices
2604            .into_iter()
2605            .map(|(n, _t)| AccountTypeName::try_from(n).unwrap())
2606            .collect::<Vec<_>>();
2607
2608        let type_by_name: HashMap<AccountTypeName<'a>, AccountType> = HashMap::from_iter(
2609            names_types
2610                .into_iter()
2611                .map(|(n, t)| (AccountTypeName::try_from(n).unwrap(), t)),
2612        );
2613
2614        AccountTypeNames {
2615            name_by_type,
2616            type_by_name,
2617        }
2618    }
2619}
2620
2621impl Display for AccountTypeNames<'_> {
2622    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2623        format(f, &self.name_by_type, plain, ", ", None)
2624    }
2625}
2626
2627#[derive(PartialEq, Eq, Debug)]
2628pub(crate) struct AccountTypeNamesError(AccountTypeNamesErrorKind);
2629
2630#[derive(PartialEq, Eq, Debug)]
2631enum AccountTypeNamesErrorKind {
2632    NameInUse(AccountType),
2633}
2634
2635impl Display for AccountTypeNamesError {
2636    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2637        use AccountTypeNamesErrorKind::*;
2638        match &self.0 {
2639            NameInUse(t) => write!(f, "account type name in use for {}", t.as_ref()),
2640        }
2641    }
2642}
2643
2644impl std::error::Error for AccountTypeNamesError {}
2645
2646mod tests;