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