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