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 AsRef<str> for Account<'_> {
1159 fn as_ref(&self) -> &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 AsRef<str> for Subaccount<'_> {
1201 fn as_ref(&self) -> &str {
1202 self.0
1203 }
1204}
1205
1206impl Display for Subaccount<'_> {
1207 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1208 write!(f, "{}", &self.0)
1209 }
1210}
1211
1212impl<'a> TryFrom<&'a str> for Subaccount<'a> {
1213 type Error = AccountNameError;
1214
1215 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1216 let mut empty = true;
1217 for account_name in s.split(':') {
1218 _ = AccountName::try_from(account_name)?;
1219 empty = false;
1220 }
1221
1222 if empty {
1223 Err(AccountNameError(AccountNameErrorKind::Empty))
1224 } else {
1225 Ok(Subaccount(s))
1226 }
1227 }
1228}
1229
1230#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1232pub struct AccountTypeName<'a>(&'a str);
1233
1234impl AccountTypeName<'_> {
1235 pub(crate) fn is_valid_initial(c: &char) -> bool {
1236 c.is_ascii_uppercase()
1237 }
1238
1239 pub(crate) fn is_valid_subsequent(c: &char) -> bool {
1240 c.is_alphanumeric() || *c == '-'
1241 }
1242}
1243
1244impl<'a> TryFrom<&'a str> for AccountTypeName<'a> {
1245 type Error = AccountTypeNameError;
1246
1247 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1248 use AccountTypeNameErrorKind::*;
1249 if s.is_empty() {
1250 Err(AccountTypeNameError(Empty))
1251 } else {
1252 let mut chars = s.chars();
1253 let initial = chars.next().unwrap();
1254 if !AccountTypeName::is_valid_initial(&initial) {
1255 Err(AccountTypeNameError(Initial(initial)))
1256 } else {
1257 let bad_chars = chars
1258 .filter(|c| !AccountTypeName::is_valid_subsequent(c))
1259 .collect::<Vec<char>>();
1260 if bad_chars.is_empty() {
1261 Ok(AccountTypeName(s))
1262 } else {
1263 Err(AccountTypeNameError(Subsequent(bad_chars)))
1264 }
1265 }
1266 }
1267 }
1268}
1269
1270impl ElementType for AccountTypeName<'_> {
1271 fn element_type(&self) -> &'static str {
1272 "account type name"
1273 }
1274}
1275
1276impl AsRef<str> for AccountTypeName<'_> {
1277 fn as_ref(&self) -> &str {
1278 self.0
1279 }
1280}
1281
1282impl PartialEq<&str> for AccountTypeName<'_> {
1283 fn eq(&self, other: &&str) -> bool {
1284 self.0 == *other
1285 }
1286}
1287
1288impl Display for AccountTypeName<'_> {
1289 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1290 write!(f, "{}", &self.0)
1291 }
1292}
1293
1294#[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 AsRef<str> for AccountName<'_> {
1352 fn as_ref(&self) -> &str {
1353 self.0
1354 }
1355}
1356
1357impl PartialEq<&str> for AccountName<'_> {
1358 fn eq(&self, other: &&str) -> bool {
1359 self.0 == *other
1360 }
1361}
1362
1363impl Display for AccountName<'_> {
1364 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1365 write!(f, "{}", &self.0)
1366 }
1367}
1368
1369#[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 AsRef<str> for Currency<'_> {
1456 fn as_ref(&self) -> &str {
1457 self.0
1458 }
1459}
1460
1461impl PartialEq<&str> for Currency<'_> {
1462 fn eq(&self, other: &&str) -> bool {
1463 self.0 == *other
1464 }
1465}
1466
1467impl Display for Currency<'_> {
1468 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1469 write!(f, "{}", &self.0)
1470 }
1471}
1472
1473#[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) TagOrLinkIdentifier<'a>);
1780
1781impl<'a> From<TagOrLinkIdentifier<'a>> for Tag<'a> {
1782 fn from(id: TagOrLinkIdentifier<'a>) -> Self {
1783 Self(id)
1784 }
1785}
1786
1787impl<'a> TryFrom<&'a str> for Tag<'a> {
1788 type Error = TagOrLinkIdentifierError;
1789
1790 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1791 TagOrLinkIdentifier::try_from(s).map(Tag)
1792 }
1793}
1794
1795impl ElementType for Tag<'_> {
1796 fn element_type(&self) -> &'static str {
1797 "tag"
1798 }
1799}
1800
1801impl AsRef<str> for Tag<'_> {
1802 fn as_ref(&self) -> &str {
1803 self.0.as_ref()
1804 }
1805}
1806
1807impl PartialEq<&str> for Tag<'_> {
1808 fn eq(&self, other: &&str) -> bool {
1809 self.0 == *other
1810 }
1811}
1812
1813impl Display for Tag<'_> {
1814 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1815 write!(f, "#{}", self.0 .0)
1816 }
1817}
1818
1819#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
1821pub struct Link<'a>(pub(crate) TagOrLinkIdentifier<'a>);
1822
1823impl<'a> From<TagOrLinkIdentifier<'a>> for Link<'a> {
1824 fn from(id: TagOrLinkIdentifier<'a>) -> Self {
1825 Self(id)
1826 }
1827}
1828
1829impl<'a> TryFrom<&'a str> for Link<'a> {
1830 type Error = TagOrLinkIdentifierError;
1831
1832 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1833 TagOrLinkIdentifier::try_from(s).map(Link)
1834 }
1835}
1836
1837impl ElementType for Link<'_> {
1838 fn element_type(&self) -> &'static str {
1839 "link"
1840 }
1841}
1842
1843impl PartialEq<&str> for Link<'_> {
1844 fn eq(&self, other: &&str) -> bool {
1845 self.0 == *other
1846 }
1847}
1848
1849impl Display for Link<'_> {
1850 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1851 write!(f, "^{}", self.0 .0)
1852 }
1853}
1854
1855impl AsRef<str> for Link<'_> {
1856 fn as_ref(&self) -> &str {
1857 self.0.as_ref()
1858 }
1859}
1860
1861#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1863pub struct TagOrLinkIdentifier<'a>(&'a str);
1864
1865const TAG_OR_LINK_EXTRA_CHARS: [char; 4] = ['-', '_', '/', '.'];
1867
1868impl TagOrLinkIdentifier<'_> {
1869 pub(crate) fn is_valid_char(c: &char) -> bool {
1870 c.is_alphanumeric() || TAG_OR_LINK_EXTRA_CHARS.contains(c)
1871 }
1872}
1873
1874#[derive(PartialEq, Eq, Debug)]
1876pub struct TagOrLinkIdentifierError(Vec<char>);
1877
1878impl Display for TagOrLinkIdentifierError {
1879 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1880 format(f, &self.0, single_quoted, ", ", Some("invalid characters "))?;
1881 format(
1882 f,
1883 TAG_OR_LINK_EXTRA_CHARS,
1884 single_quoted,
1885 ", ",
1886 Some(" for tag or link identifier - must be alphanumeric or one of "),
1887 )
1888 }
1889}
1890
1891impl std::error::Error for TagOrLinkIdentifierError {}
1892
1893impl<'a> TryFrom<&'a str> for TagOrLinkIdentifier<'a> {
1894 type Error = TagOrLinkIdentifierError;
1895
1896 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
1897 let bad_chars = s
1898 .chars()
1899 .filter(|c| !TagOrLinkIdentifier::is_valid_char(c))
1900 .collect::<Vec<char>>();
1901 if bad_chars.is_empty() {
1902 Ok(TagOrLinkIdentifier(s))
1903 } else {
1904 Err(TagOrLinkIdentifierError(bad_chars))
1905 }
1906 }
1907}
1908
1909impl AsRef<str> for TagOrLinkIdentifier<'_> {
1910 fn as_ref(&self) -> &str {
1911 self.0
1912 }
1913}
1914
1915impl PartialEq<&str> for TagOrLinkIdentifier<'_> {
1916 fn eq(&self, other: &&str) -> bool {
1917 self.0 == *other
1918 }
1919}
1920
1921#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Copy, Clone, Debug)]
1923pub struct Key<'a>(&'a str);
1924
1925impl Key<'_> {
1926 pub(crate) fn is_valid_initial(c: &char) -> bool {
1927 c.is_ascii_lowercase()
1928 }
1929
1930 pub(crate) fn is_valid_subsequent(c: &char) -> bool {
1931 c.is_alphanumeric() || *c == '-' || *c == '_'
1932 }
1933}
1934
1935impl AsRef<str> for Key<'_> {
1936 fn as_ref(&self) -> &str {
1937 self.0
1938 }
1939}
1940
1941impl ElementType for Key<'_> {
1942 fn element_type(&self) -> &'static str {
1943 "key"
1944 }
1945}
1946
1947impl PartialEq<&str> for Key<'_> {
1948 fn eq(&self, other: &&str) -> bool {
1949 self.0 == *other
1950 }
1951}
1952
1953impl Display for Key<'_> {
1954 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1955 write!(f, "{}", &self.0)
1956 }
1957}
1958
1959#[derive(PartialEq, Eq, Debug)]
1961pub struct KeyError(KeyErrorKind);
1962
1963#[derive(PartialEq, Eq, Debug)]
1964enum KeyErrorKind {
1965 Empty,
1966 Initial(char),
1967 Subsequent(Vec<char>),
1968}
1969
1970impl Display for KeyError {
1971 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1972 use KeyErrorKind::*;
1973 match &self.0 {
1974 Empty => write!(f, "empty key"),
1975 Initial(bad_char) => write!(
1976 f,
1977 "invalid character '{}' for key initial - must be lowercase ASCII letter",
1978 bad_char
1979 ),
1980 Subsequent(bad_chars) => {
1981 format(
1982 f,
1983 bad_chars,
1984 single_quoted,
1985 ", ",
1986 Some("invalid characters "),
1987 )?;
1988 f.write_str(" for key - must be alphanumeric or '-' or '_'")
1989 }
1990 }
1991 }
1992}
1993
1994impl std::error::Error for KeyError {}
1995
1996impl<'a> TryFrom<&'a str> for Key<'a> {
1997 type Error = KeyError;
1998
1999 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
2000 use KeyErrorKind::*;
2001 if s.is_empty() {
2002 Err(KeyError(Empty))
2003 } else {
2004 let mut chars = s.chars();
2005 let initial = chars.next().unwrap();
2006 if !Key::is_valid_initial(&initial) {
2007 Err(KeyError(Initial(initial)))
2008 } else {
2009 let bad_chars = chars
2010 .filter(|c| !Key::is_valid_subsequent(c))
2011 .collect::<Vec<char>>();
2012 if bad_chars.is_empty() {
2013 Ok(Key(s))
2014 } else {
2015 Err(KeyError(Subsequent(bad_chars)))
2016 }
2017 }
2018 }
2019 }
2020}
2021
2022#[derive(PartialEq, Eq, Clone, Debug)]
2023pub struct ExprValue {
2027 value: Decimal,
2028 expr: Expr,
2029}
2030
2031impl ExprValue {
2032 pub fn value(&self) -> Decimal {
2034 self.value
2035 }
2036
2037 pub fn expr(&self) -> &Expr {
2039 &self.expr
2040 }
2041}
2042
2043impl PartialEq<Decimal> for ExprValue {
2044 fn eq(&self, other: &Decimal) -> bool {
2045 &self.value == other
2046 }
2047}
2048
2049impl From<Expr> for ExprValue {
2050 fn from(expr: Expr) -> Self {
2052 let (mut value, scale) = expr.evaluate();
2053 value.rescale(scale);
2054 Self { value, expr }
2055 }
2056}
2057
2058impl ElementType for ExprValue {
2060 fn element_type(&self) -> &'static str {
2061 "amount" }
2063}
2064
2065impl Display for ExprValue {
2066 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2067 Display::fmt(&self.expr, format)
2068 }
2069}
2070
2071#[derive(PartialEq, Eq, Clone)]
2073pub enum Expr {
2074 Value(Decimal),
2075 Add(Box<Expr>, Box<Expr>),
2076 Sub(Box<Expr>, Box<Expr>),
2077 Mul(Box<Expr>, Box<Expr>),
2078 Div(Box<Expr>, Box<Expr>),
2079 Neg(Box<Expr>),
2080 Paren(Box<Expr>),
2081}
2082
2083impl Expr {
2084 fn evaluate(&self) -> (Decimal, u32) {
2085 fn evaluate_unary<F>(op: F, e1: &Expr) -> (Decimal, u32)
2086 where
2087 F: Fn(Decimal) -> Decimal,
2088 {
2089 let (d1, s1) = e1.evaluate();
2090 (op(d1), s1)
2091 }
2092
2093 fn evaluate_binary<F>(op: F, e1: &Expr, e2: &Expr) -> (Decimal, u32)
2094 where
2095 F: Fn(Decimal, Decimal) -> Decimal,
2096 {
2097 let (d1, s1) = e1.evaluate();
2098 let (d2, s2) = e2.evaluate();
2099 (op(d1, d2), max(s1, s2))
2100 }
2101
2102 use Expr::*;
2103 match self {
2104 Value(d) => (*d, d.scale()),
2105 Add(e1, e2) => evaluate_binary(std::ops::Add::add, e1, e2),
2106 Sub(e1, e2) => evaluate_binary(std::ops::Sub::sub, e1, e2),
2107 Mul(e1, e2) => evaluate_binary(std::ops::Mul::mul, e1, e2),
2108 Div(e1, e2) => evaluate_binary(std::ops::Div::div, e1, e2),
2109 Neg(e1) => evaluate_unary(std::ops::Neg::neg, e1),
2110 Paren(e) => e.evaluate(),
2111 }
2112 }
2113}
2114
2115impl Display for Expr {
2120 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2121 use self::Expr::*;
2122 match *self {
2123 Value(val) => write!(format, "{}", val),
2124 Add(ref left, ref right) => write!(format, "{} + {}", left, right),
2125 Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
2126 Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
2127 Div(ref left, ref right) => write!(format, "{} / {}", left, right),
2128 Neg(ref expr) => write!(format, "-{}", expr),
2129 Paren(ref expr) => write!(format, "({})", expr),
2130 }
2131 }
2132}
2133
2134impl fmt::Debug for Expr {
2135 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2136 use self::Expr::*;
2137 match *self {
2138 Value(val) => write!(format, "{}", val),
2139 Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
2140 Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
2141 Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
2142 Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
2143 Neg(ref expr) => write!(format, "(-{:?})", expr),
2144 Paren(ref expr) => write!(format, "[{:?}]", expr),
2145 }
2146 }
2147}
2148
2149#[derive(PartialEq, Eq, Clone, Debug)]
2151pub enum CompoundExprValue {
2152 PerUnit(ExprValue),
2153 Total(ExprValue),
2154 PerUnitAndTotal(ExprValue, ExprValue),
2155}
2156
2157impl Display for CompoundExprValue {
2158 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2159 use self::CompoundExprValue::*;
2160
2161 match self {
2162 PerUnit(e) => write!(format, "{}", e),
2163 Total(e) => write!(format, "# {}", e),
2164 PerUnitAndTotal(per_unit, total) => write!(format, "{} # {}", per_unit, total),
2165 }
2166 }
2167}
2168
2169#[derive(PartialEq, Eq, Clone, Debug)]
2171pub enum ScopedExprValue {
2172 PerUnit(ExprValue),
2173 Total(ExprValue),
2174}
2175
2176impl Display for ScopedExprValue {
2177 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2178 use self::ScopedExprValue::*;
2179
2180 match self {
2181 PerUnit(e) => write!(format, "{}", e),
2182 Total(e) => write!(format, "# {}", e),
2183 }
2184 }
2185}
2186
2187#[derive(PartialEq, Eq, Clone, Debug)]
2189pub struct Amount<'a> {
2190 number: Spanned<ExprValue>,
2191 currency: Spanned<Currency<'a>>,
2192}
2193
2194impl<'a> Amount<'a> {
2195 pub(crate) fn new(amount: (Spanned<ExprValue>, Spanned<Currency<'a>>)) -> Self {
2196 Amount {
2197 number: amount.0,
2198 currency: amount.1,
2199 }
2200 }
2201
2202 pub fn number(&self) -> &Spanned<ExprValue> {
2204 &self.number
2205 }
2206
2207 pub fn currency(&self) -> &Spanned<Currency<'a>> {
2209 &self.currency
2210 }
2211}
2212
2213impl Display for Amount<'_> {
2214 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2215 write!(format, "{} {}", &self.number, &self.currency)
2216 }
2217}
2218
2219#[derive(PartialEq, Eq, Clone, Debug)]
2221pub struct AmountWithTolerance<'a> {
2222 amount: Spanned<Amount<'a>>,
2223 tolerance: Option<Spanned<Decimal>>,
2224}
2225
2226impl<'a> AmountWithTolerance<'a> {
2227 pub(crate) fn new(awt: (Spanned<Amount<'a>>, Option<Spanned<Decimal>>)) -> Self {
2228 AmountWithTolerance {
2229 amount: awt.0,
2230 tolerance: awt.1,
2231 }
2232 }
2233
2234 pub fn amount(&self) -> &Spanned<Amount<'a>> {
2236 &self.amount
2237 }
2238
2239 pub fn tolerance(&self) -> Option<&Spanned<Decimal>> {
2241 self.tolerance.as_ref()
2242 }
2243}
2244
2245impl Display for AmountWithTolerance<'_> {
2246 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2247 if let Some(tolerance) = self.tolerance {
2248 write!(format, "{} ~ {}", &self.amount, tolerance)
2249 } else {
2250 write!(format, "{}", &self.amount)
2251 }
2252 }
2253}
2254
2255#[derive(PartialEq, Eq, Clone, Debug)]
2257pub struct LooseAmount<'a> {
2258 number: Option<Spanned<ExprValue>>,
2259 currency: Option<Spanned<Currency<'a>>>,
2260}
2261
2262impl<'a> LooseAmount<'a> {
2263 pub(crate) fn new(amount: (Option<Spanned<ExprValue>>, Option<Spanned<Currency<'a>>>)) -> Self {
2264 LooseAmount {
2265 number: amount.0,
2266 currency: amount.1,
2267 }
2268 }
2269
2270 pub fn number(&self) -> Option<&Spanned<ExprValue>> {
2272 self.number.as_ref()
2273 }
2274
2275 pub fn currency(&self) -> Option<&Spanned<Currency<'a>>> {
2277 self.currency.as_ref()
2278 }
2279}
2280
2281#[derive(PartialEq, Eq, Clone, Debug)]
2283pub enum CompoundAmount<'a> {
2284 BareCurrency(Currency<'a>),
2285 BareAmount(CompoundExprValue),
2286 CurrencyAmount(CompoundExprValue, Currency<'a>),
2287}
2288
2289impl Display for CompoundAmount<'_> {
2290 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2291 use self::CompoundAmount::*;
2292 match self {
2293 BareCurrency(cur) => write!(format, "{}", cur),
2294 BareAmount(ce) => write!(format, "{}", ce),
2295 CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2296 }
2297 }
2298}
2299
2300#[derive(PartialEq, Eq, Clone, Debug)]
2302pub struct CostSpec<'a> {
2303 per_unit: Option<Spanned<ExprValue>>,
2304 total: Option<Spanned<ExprValue>>,
2305 currency: Option<Spanned<Currency<'a>>>,
2306 date: Option<Spanned<Date>>,
2307 label: Option<Spanned<&'a str>>,
2308 merge: bool,
2309}
2310
2311impl CostSpec<'_> {
2312 pub fn per_unit(&self) -> Option<&Spanned<ExprValue>> {
2314 self.per_unit.as_ref()
2315 }
2316
2317 pub fn total(&self) -> Option<&Spanned<ExprValue>> {
2319 self.total.as_ref()
2320 }
2321
2322 pub fn currency(&self) -> Option<&Spanned<Currency<'_>>> {
2324 self.currency.as_ref()
2325 }
2326
2327 pub fn date(&self) -> Option<&Spanned<Date>> {
2329 self.date.as_ref()
2330 }
2331
2332 pub fn label(&self) -> Option<&Spanned<&str>> {
2334 self.label.as_ref()
2335 }
2336
2337 pub fn merge(&self) -> bool {
2339 self.merge
2340 }
2341}
2342
2343impl ElementType for CostSpec<'_> {
2344 fn element_type(&self) -> &'static str {
2345 "cost specification"
2346 }
2347}
2348
2349impl Display for CostSpec<'_> {
2350 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2351 let mut prefix = "";
2352 let space = " ";
2353
2354 f.write_str("{")?;
2355
2356 if let Some(per_unit) = &self.per_unit {
2357 write!(f, "{}{}", prefix, per_unit)?;
2358 prefix = space;
2359 }
2360
2361 if let Some(total) = &self.total {
2362 write!(f, "{}# {}", prefix, total)?;
2363 prefix = space;
2364 }
2365
2366 if let Some(currency) = &self.currency {
2367 write!(f, "{}{}", prefix, currency)?;
2368 prefix = space;
2369 }
2370
2371 if let Some(date) = &self.date {
2372 write!(f, "{}{}", prefix, date)?;
2373 prefix = space;
2374 }
2375
2376 if let Some(label) = &self.label {
2377 write!(f, "{}\"{}\"", prefix, label)?;
2378 prefix = space;
2379 }
2380
2381 if self.merge {
2382 write!(f, "{}*", prefix)?;
2383 }
2384
2385 f.write_str("}")
2386 }
2387}
2388
2389#[derive(Default, Debug)]
2390pub(crate) struct CostSpecBuilder<'a> {
2392 per_unit: Option<Spanned<ExprValue>>,
2393 total: Option<Spanned<ExprValue>>,
2394 currency: Option<Spanned<Currency<'a>>>,
2395 date: Option<Spanned<Date>>,
2396 label: Option<Spanned<&'a str>>,
2397 merge: bool,
2398 errors: Vec<CostSpecError>,
2399}
2400
2401impl<'a> CostSpecBuilder<'a> {
2402 pub(crate) fn compound_expr(self, value: CompoundExprValue, span: Span) -> Self {
2403 use CompoundExprValue::*;
2404
2405 match value {
2406 PerUnit(value) => self.per_unit(spanned(value, span)),
2407 Total(value) => self.total(spanned(value, span)),
2408 PerUnitAndTotal(per_unit, total) => self
2409 .per_unit(spanned(per_unit, span))
2410 .total(spanned(total, span)),
2411 }
2412 }
2413
2414 fn per_unit(mut self, value: Spanned<ExprValue>) -> Self {
2415 if self.per_unit.is_none() {
2416 self.per_unit = Some(value);
2417 } else {
2418 self.errors.push(CostSpecError(CostSpecErrorKind::PerUnit))
2419 }
2420 self
2421 }
2422
2423 fn total(mut self, value: Spanned<ExprValue>) -> Self {
2424 if self.total.is_none() {
2425 self.total = Some(value);
2426 } else {
2427 self.errors.push(CostSpecError(CostSpecErrorKind::Total))
2428 }
2429 self
2430 }
2431
2432 pub(crate) fn currency(mut self, value: Currency<'a>, span: Span) -> Self {
2433 if self.currency.is_none() {
2434 self.currency = Some(spanned(value, span));
2435 } else {
2436 self.errors.push(CostSpecError(CostSpecErrorKind::Currency))
2437 }
2438 self
2439 }
2440
2441 pub(crate) fn date(mut self, value: Date, span: Span) -> Self {
2442 if self.date.is_none() {
2443 self.date = Some(spanned(value, span));
2444 } else {
2445 self.errors.push(CostSpecError(CostSpecErrorKind::Date))
2446 }
2447 self
2448 }
2449
2450 pub(crate) fn label(mut self, value: &'a str, span: Span) -> Self {
2451 if self.label.is_none() {
2452 self.label = Some(spanned(value, span));
2453 } else {
2454 self.errors.push(CostSpecError(CostSpecErrorKind::Label))
2455 }
2456 self
2457 }
2458
2459 pub(crate) fn merge(mut self, _span: Span) -> Self {
2460 if !self.merge {
2461 self.merge = true;
2463 } else {
2464 self.errors
2465 .push(CostSpecError(CostSpecErrorKind::MergeCost))
2466 }
2467 self
2468 }
2469
2470 pub(crate) fn build<'b>(&'b mut self) -> Result<CostSpec<'a>, CostSpecErrors>
2472 where
2473 'a: 'b,
2474 {
2475 let per_unit = self.per_unit.take();
2476 let total = self.total.take();
2477 let currency = self.currency.take();
2478 let date = self.date.take();
2479 let label = self.label.take();
2480 let merge = self.merge;
2481 self.merge = false;
2482
2483 if !self.errors.is_empty() {
2484 let mut errors = Vec::new();
2485 swap(&mut self.errors, &mut errors);
2486 Err(CostSpecErrors(errors))
2487 } else {
2488 Ok(CostSpec {
2489 per_unit,
2490 total,
2491 currency,
2492 date,
2493 label,
2494 merge,
2495 })
2496 }
2497 }
2499}
2500
2501#[derive(PartialEq, Eq, Debug)]
2503pub struct CostSpecError(CostSpecErrorKind);
2504
2505#[derive(PartialEq, Eq, Display, Debug)]
2506#[strum(serialize_all = "kebab-case")]
2507enum CostSpecErrorKind {
2508 PerUnit,
2509 Total,
2510 Currency,
2511 Date,
2512 Label,
2513 MergeCost,
2514}
2515
2516impl CostSpecErrorKind {
2517 fn unless(self, condition: bool) -> Result<(), CostSpecError> {
2518 if condition {
2519 Ok(())
2520 } else {
2521 Err(CostSpecError(self))
2522 }
2523 }
2524}
2525
2526impl Display for CostSpecError {
2527 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2528 write!(f, "duplicate {} field in cost specification", self.0)
2529 }
2530}
2531
2532impl std::error::Error for CostSpecError {}
2533
2534#[derive(PartialEq, Eq, Debug)]
2536pub struct CostSpecErrors(Vec<CostSpecError>);
2537
2538impl Display for CostSpecErrors {
2539 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2540 format(f, &self.0, plain, ", ", None)
2541 }
2542}
2543
2544impl std::error::Error for CostSpecErrors {}
2545
2546#[derive(PartialEq, Eq, Clone, Debug)]
2549pub enum PriceSpec<'a> {
2550 Unspecified,
2551 BareCurrency(Currency<'a>),
2552 BareAmount(ScopedExprValue),
2553 CurrencyAmount(ScopedExprValue, Currency<'a>),
2554}
2555
2556impl Display for PriceSpec<'_> {
2557 fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
2558 use self::PriceSpec::*;
2559 match self {
2560 Unspecified => Ok(()),
2561 BareCurrency(cur) => write!(format, "{}", cur),
2562 BareAmount(ce) => write!(format, "{}", ce),
2563 CurrencyAmount(ce, cur) => write!(format, "{} {}", ce, cur),
2564 }
2565 }
2566}
2567
2568#[derive(Debug)]
2569pub(crate) struct AccountTypeNames<'a> {
2570 pub(crate) name_by_type: Vec<AccountTypeName<'a>>,
2572 pub(crate) type_by_name: HashMap<AccountTypeName<'a>, AccountType>,
2573}
2574
2575impl<'a> AccountTypeNames<'a> {
2576 pub(crate) fn name(&self, account_type: AccountType) -> AccountTypeName<'a> {
2577 self.name_by_type[account_type as usize]
2578 }
2579
2580 pub(crate) fn get(&self, name: &AccountTypeName) -> Option<AccountType> {
2581 self.type_by_name.get(name).copied()
2582 }
2583
2584 pub(crate) fn update(
2585 &mut self,
2586 account_type: AccountType,
2587 name: AccountTypeName<'a>,
2588 ) -> Result<(), AccountTypeNamesError> {
2589 use hash_map::Entry::*;
2590
2591 match self.type_by_name.entry(name) {
2592 Vacant(e) => {
2593 e.insert(account_type);
2594 let old_name = self.name_by_type[account_type as usize];
2595 self.name_by_type[account_type as usize] = name;
2596 self.type_by_name.remove(&old_name);
2597 Ok(())
2598 }
2599 Occupied(o) => {
2600 let existing_account_type = *o.get();
2601 if existing_account_type == account_type {
2602 Ok(())
2604 } else {
2605 Err(AccountTypeNamesError(AccountTypeNamesErrorKind::NameInUse(
2606 existing_account_type,
2607 )))
2608 }
2609 }
2610 }
2611 }
2612}
2613
2614impl<'a> Default for AccountTypeNames<'a> {
2615 fn default() -> Self {
2616 use AccountType::*;
2617
2618 let names_types = vec![
2619 ("Assets", Assets),
2620 ("Liabilities", Liabilities),
2621 ("Equity", Equity),
2622 ("Income", Income),
2623 ("Expenses", Expenses),
2624 ];
2625
2626 let mut names_type_indices = names_types
2627 .iter()
2628 .map(|(n, t)| (*n, *t as usize))
2629 .collect::<Vec<_>>();
2630 names_type_indices.sort_by_key(|(_n, t)| *t);
2631
2632 let name_by_type = names_type_indices
2633 .into_iter()
2634 .map(|(n, _t)| AccountTypeName::try_from(n).unwrap())
2635 .collect::<Vec<_>>();
2636
2637 let type_by_name: HashMap<AccountTypeName<'a>, AccountType> = HashMap::from_iter(
2638 names_types
2639 .into_iter()
2640 .map(|(n, t)| (AccountTypeName::try_from(n).unwrap(), t)),
2641 );
2642
2643 AccountTypeNames {
2644 name_by_type,
2645 type_by_name,
2646 }
2647 }
2648}
2649
2650impl Display for AccountTypeNames<'_> {
2651 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2652 format(f, &self.name_by_type, plain, ", ", None)
2653 }
2654}
2655
2656#[derive(PartialEq, Eq, Debug)]
2657pub(crate) struct AccountTypeNamesError(AccountTypeNamesErrorKind);
2658
2659#[derive(PartialEq, Eq, Debug)]
2660enum AccountTypeNamesErrorKind {
2661 NameInUse(AccountType),
2662}
2663
2664impl Display for AccountTypeNamesError {
2665 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
2666 use AccountTypeNamesErrorKind::*;
2667 match &self.0 {
2668 NameInUse(t) => write!(f, "account type name in use for {}", t.as_ref()),
2669 }
2670 }
2671}
2672
2673impl std::error::Error for AccountTypeNamesError {}
2674
2675mod tests;