Skip to main content

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