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