bean_rs/
data.rs

1/// Only those types in the enum Directives are direct members of the Ledger.
2/// The rest are children of other elements.
3use std::collections::HashMap;
4use std::fmt;
5
6use chrono::NaiveDate;
7use pest::iterators::{Pair, Pairs};
8use pyo3::pyclass;
9use rust_decimal::Decimal;
10
11use crate::grammar::Rule;
12
13const BASE_DATE: &str = "0001-01-01";
14pub const DATE_FMT: &str = "%Y-%m-%d";
15
16type Ccy = String;
17pub type Account = String;
18
19pub type CcyBal = HashMap<Ccy, Decimal>;
20pub type AccBal = HashMap<Account, CcyBal>;
21pub type AccStatuses = HashMap<Account, (bool, Vec<Ccy>)>;
22
23#[pyclass]
24#[derive(Clone, Debug)]
25pub struct Options {
26    #[pyo3(get)]
27    pub title: String,
28    #[pyo3(get)]
29    pub operating_currency: String,
30}
31
32impl Default for Options {
33    fn default() -> Self {
34        Self {
35            title: "".to_string(),
36            operating_currency: "".to_string(),
37        }
38    }
39}
40
41impl Options {
42    pub fn update_from_entry(&mut self, entry: Pair<Rule>) {
43        let mut pairs = entry.clone().into_inner();
44        let key = pairs.next().unwrap().as_str();
45        let val = pairs.next().unwrap().as_str().to_string();
46        match key {
47            "title" => self.title = val,
48            "operating_currency" => self.operating_currency = val,
49            _ => panic!("Other options not handled yet"),
50        }
51    }
52}
53
54#[derive(Clone, Debug, Default)]
55pub struct DebugLine {
56    pub line: usize,
57}
58
59impl DebugLine {
60    pub fn new(line: usize) -> Self {
61        Self { line }
62    }
63}
64
65impl PartialEq for DebugLine {
66    fn eq(&self, _: &Self) -> bool {
67        true
68    }
69}
70
71impl fmt::Display for DebugLine {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        write!(f, "line:{line}", line = self.line)
74    }
75}
76
77#[derive(Clone, Debug)]
78pub struct Amount {
79    pub number: Decimal,
80    pub ccy: Ccy,
81}
82
83impl PartialEq for Amount {
84    fn eq(&self, other: &Self) -> bool {
85        // TODO get precision from context
86        self.ccy == other.ccy && (self.number - other.number).abs() > Decimal::new(1, 3)
87    }
88}
89impl Eq for Amount {}
90
91impl fmt::Display for Amount {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        write!(f, "{number} {ccy}", number = self.number, ccy = self.ccy,)
94    }
95}
96
97impl Amount {
98    pub fn new(number: Decimal, ccy: Ccy) -> Self {
99        Self { number, ccy }
100    }
101    pub fn from_entry(entry: Pair<Rule>) -> Self {
102        let mut pairs = entry.clone().into_inner();
103        let mut number: String = pairs.next().unwrap().as_str().to_string();
104        if number.contains(',') {
105            number = number.replace(',', "");
106        }
107        let number: Decimal = match number.parse() {
108            Ok(num) => num,
109            Err(_) => {
110                let (line, _) = entry.line_col();
111                panic!("Un-parseable decimal at line:{line}");
112            }
113        };
114        let ccy = pairs.next().unwrap().as_str().to_string();
115        Self { number, ccy }
116    }
117}
118
119#[derive(Clone, Debug, PartialEq)]
120pub struct ConfigCustom {
121    pub date: NaiveDate,
122    pub debug: DebugLine,
123}
124
125impl ConfigCustom {
126    pub fn from_entry(entry: Pair<Rule>) -> Self {
127        let (line, _) = entry.line_col();
128        let debug = DebugLine { line };
129        let date = NaiveDate::parse_from_str(BASE_DATE, DATE_FMT).unwrap();
130        Self { date, debug }
131    }
132}
133
134impl fmt::Display for ConfigCustom {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        write!(f, "-- ignore custom")
137    }
138}
139
140#[derive(Debug, Clone, PartialEq)]
141pub struct Metadata {
142    pub key: String,
143    pub val: String,
144    pub debug: DebugLine,
145}
146
147impl Metadata {
148    pub fn from_entry(entry: Pair<Rule>) -> Self {
149        let mut pairs = entry.clone().into_inner();
150        let key = pairs.next().unwrap().as_str().to_string();
151        let val = pairs.next().unwrap().as_str().to_string();
152        let (line, _) = entry.line_col();
153        let debug = DebugLine { line };
154        Self { key, val, debug }
155    }
156}
157
158impl fmt::Display for Metadata {
159    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160        write!(f, "  {key}:{val}", key = self.key, val = self.val,)
161    }
162}
163
164#[derive(Clone, Debug, PartialEq)]
165pub struct Commodity {
166    pub date: NaiveDate,
167    pub ccy: String,
168    pub meta: Vec<Metadata>,
169    pub debug: DebugLine,
170}
171
172impl Commodity {
173    pub fn from_entry(entry: Pair<Rule>) -> Self {
174        let mut pairs = entry.clone().into_inner();
175        let date = pairs.next().unwrap().as_str();
176        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
177        let ccy = pairs.next().unwrap().as_str().to_string();
178        let mut meta: Vec<Metadata> = Vec::new();
179        for pair in pairs {
180            if pair.as_rule() == Rule::metadata {
181                let p = Metadata::from_entry(pair);
182                meta.push(p)
183            }
184        }
185        let (line, _) = entry.line_col();
186        let debug = DebugLine { line };
187        Self {
188            date,
189            ccy,
190            meta,
191            debug,
192        }
193    }
194}
195
196impl fmt::Display for Commodity {
197    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198        let mut meta_string: String = String::new();
199        let m_slice = &self.meta[..];
200        for m in m_slice {
201            let line: &str = &format!("\n{m}");
202            meta_string.push_str(line);
203        }
204        write!(
205            f,
206            "{date} commodity {ccy}{meta}",
207            date = self.date,
208            ccy = self.ccy,
209            meta = meta_string,
210        )
211    }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub struct Open {
216    pub date: NaiveDate,
217    pub account: Account,
218    pub ccys: Vec<Ccy>,
219    pub meta: Vec<Metadata>,
220    pub debug: DebugLine,
221}
222
223impl Open {
224    pub fn from_entry(entry: Pair<Rule>) -> Self {
225        let mut pairs = entry.clone().into_inner();
226        let date = pairs.next().unwrap().as_str();
227        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
228        let account = pairs.next().unwrap().as_str().to_string();
229        let (line, _) = entry.line_col();
230        let debug = DebugLine { line };
231
232        let mut ccys: Vec<Ccy> = Vec::new();
233        let mut meta: Vec<Metadata> = Vec::new();
234
235        for pair in pairs {
236            match pair.as_rule() {
237                Rule::ccy => {
238                    let c = pair.as_str().to_owned();
239                    ccys.push(c);
240                }
241                Rule::metadata => {
242                    let m = Metadata::from_entry(pair);
243                    meta.push(m);
244                }
245                _ => (),
246            }
247        }
248
249        Self {
250            date,
251            account,
252            ccys,
253            meta,
254            debug,
255        }
256    }
257}
258
259impl fmt::Display for Open {
260    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261        write!(
262            f,
263            "{date} {account}",
264            date = self.date,
265            account = self.account,
266        )
267    }
268}
269
270#[derive(Debug, Clone, PartialEq)]
271pub struct Close {
272    pub date: NaiveDate,
273    pub account: Account,
274    // TODO can also have Meta
275    pub debug: DebugLine,
276}
277
278impl Close {
279    pub fn from_entry(entry: Pair<Rule>) -> Self {
280        let mut pairs = entry.clone().into_inner();
281        let date = pairs.next().unwrap().as_str();
282        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
283        let account = pairs.next().unwrap().as_str().to_string();
284        let (line, _) = entry.line_col();
285        let debug = DebugLine { line };
286        Self {
287            date,
288            account,
289            debug,
290        }
291    }
292}
293
294impl fmt::Display for Close {
295    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        write!(
297            f,
298            "{date} {account}",
299            date = self.date,
300            account = self.account,
301        )
302    }
303}
304
305#[derive(Debug, Clone, PartialEq)]
306pub struct Balance {
307    pub date: NaiveDate,
308    pub account: Account,
309    pub amount: Amount,
310    // TODO can also have Meta
311    pub debug: DebugLine,
312}
313
314impl Balance {
315    pub fn from_entry(entry: Pair<Rule>) -> Self {
316        let mut pairs = entry.clone().into_inner();
317        let date = pairs.next().unwrap().as_str();
318        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
319        let account = pairs.next().unwrap().as_str().to_string();
320        let amount_entry = pairs.next().unwrap();
321        let amount = Amount::from_entry(amount_entry);
322        let (line, _) = entry.line_col();
323        let debug = DebugLine { line };
324        Self {
325            date,
326            account,
327            amount,
328            debug,
329        }
330    }
331}
332
333impl fmt::Display for Balance {
334    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
335        write!(
336            f,
337            "{date} {account} {amount}",
338            date = self.date,
339            account = self.account,
340            amount = self.amount,
341        )
342    }
343}
344
345#[derive(Debug, Clone, PartialEq)]
346pub struct Pad {
347    pub date: NaiveDate,
348    pub account_to: Account,
349    pub account_from: Account,
350    // TODO can also have Meta
351    pub debug: DebugLine,
352}
353
354impl Pad {
355    pub fn from_entry(entry: Pair<Rule>) -> Self {
356        let mut pairs = entry.clone().into_inner();
357        let date = pairs.next().unwrap().as_str();
358        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
359        let account_to = pairs.next().unwrap().as_str().to_string();
360        let account_from = pairs.next().unwrap().as_str().to_string();
361        let (line, _) = entry.line_col();
362        let debug = DebugLine { line };
363        Self {
364            date,
365            account_to,
366            account_from,
367            debug,
368        }
369    }
370}
371
372impl fmt::Display for Pad {
373    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374        write!(
375            f,
376            "{date} {account_to} {account_from}",
377            date = self.date,
378            account_to = self.account_to,
379            account_from = self.account_from,
380        )
381    }
382}
383
384#[derive(Clone, Debug, PartialEq)]
385pub struct Price {
386    pub date: NaiveDate,
387    pub commodity: String,
388    pub amount: Amount,
389    // TODO can also have Meta
390    pub debug: DebugLine,
391}
392
393impl Price {
394    pub fn from_entry(entry: Pair<Rule>) -> Self {
395        let mut pairs = entry.clone().into_inner();
396        let date = pairs.next().unwrap().as_str();
397        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
398        let commodity = pairs.next().unwrap().as_str().to_string();
399        let amount_entry = pairs.next().unwrap();
400        let amount = Amount::from_entry(amount_entry);
401        let (line, _) = entry.line_col();
402        let debug = DebugLine { line };
403        Self {
404            date,
405            commodity,
406            amount,
407            debug,
408        }
409    }
410}
411
412impl fmt::Display for Price {
413    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414        write!(
415            f,
416            "{date} {commodity} {amount}",
417            date = self.date,
418            commodity = self.commodity,
419            amount = self.amount,
420        )
421    }
422}
423
424#[derive(Clone, Debug, PartialEq)]
425pub struct Document {
426    pub date: NaiveDate,
427    pub account: Account,
428    pub path: String,
429    // TODO can also have Meta
430    pub debug: DebugLine,
431}
432
433impl Document {
434    pub fn from_entry(entry: Pair<Rule>) -> Self {
435        let mut pairs = entry.clone().into_inner();
436        let date = pairs.next().unwrap().as_str();
437        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
438        let account = pairs.next().unwrap().as_str().to_string();
439        let path = pairs.next().unwrap().as_str().to_string();
440        let (line, _) = entry.line_col();
441        let debug = DebugLine { line };
442        Self {
443            date,
444            account,
445            path,
446            debug,
447        }
448    }
449}
450
451impl fmt::Display for Document {
452    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
453        write!(
454            f,
455            "{date} document {account} {path}",
456            date = self.date,
457            account = self.account,
458            path = self.path,
459        )
460    }
461}
462
463#[derive(Clone, Debug, PartialEq)]
464pub struct Note {
465    pub date: NaiveDate,
466    pub account: Account,
467    pub note: String,
468    // TODO can also have Meta
469    pub debug: DebugLine,
470}
471
472impl Note {
473    pub fn from_entry(entry: Pair<Rule>) -> Self {
474        let mut pairs = entry.clone().into_inner();
475        let date = pairs.next().unwrap().as_str();
476        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
477        let account = pairs.next().unwrap().as_str().to_string();
478        let note = pairs.next().unwrap().as_str().to_string();
479        let (line, _) = entry.line_col();
480        let debug = DebugLine { line };
481        Self {
482            date,
483            account,
484            note,
485            debug,
486        }
487    }
488}
489
490impl fmt::Display for Note {
491    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
492        write!(
493            f,
494            "{date} note {account} {note}",
495            date = self.date,
496            account = self.account,
497            note = self.note,
498        )
499    }
500}
501
502#[derive(Clone, Debug, PartialEq)]
503pub struct Query {
504    pub date: NaiveDate,
505    pub name: String,
506    pub query: String,
507    pub debug: DebugLine,
508}
509
510impl Query {
511    pub fn from_entry(entry: Pair<Rule>) -> Self {
512        let mut pairs = entry.clone().into_inner();
513        let date = pairs.next().unwrap().as_str();
514        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
515        let name = pairs.next().unwrap().as_str().to_string();
516        let query = pairs.next().unwrap().as_str().to_string();
517        let (line, _) = entry.line_col();
518        let debug = DebugLine { line };
519        Self {
520            date,
521            name,
522            query,
523            debug,
524        }
525    }
526}
527
528impl fmt::Display for Query {
529    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
530        write!(
531            f,
532            "{date} query {name} {query}",
533            date = self.date,
534            name = self.name,
535            query = self.query,
536        )
537    }
538}
539
540#[derive(Clone, Debug, PartialEq)]
541pub struct Posting {
542    pub account: Account,
543    pub amount: Option<Amount>,
544    pub debug: Option<DebugLine>,
545}
546
547impl Posting {
548    pub fn new(account: Account, number: Decimal, ccy: Ccy) -> Self {
549        let amount = Some(Amount { number, ccy });
550        let debug = Default::default();
551        Self {
552            account,
553            amount,
554            debug,
555        }
556    }
557    pub fn from_entry(entry: Pair<Rule>) -> Self {
558        let mut pairs = entry.clone().into_inner();
559        let account = pairs.next().unwrap().as_str().to_string();
560        let amount = if pairs.peek().is_some() {
561            Some(Amount::from_entry(pairs.next().unwrap()))
562        } else {
563            None
564        };
565        let (line, _) = entry.line_col();
566        let debug = Some(DebugLine { line });
567        Self {
568            account,
569            amount,
570            debug,
571        }
572    }
573}
574
575impl fmt::Display for Posting {
576    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
577        let amount_str = match &self.amount {
578            Some(amount) => amount.to_string(),
579            None => String::new(),
580        };
581
582        write!(
583            f,
584            "  {account} {amount}",
585            account = self.account,
586            amount = amount_str,
587        )
588    }
589}
590
591#[derive(Debug, Clone, PartialEq)]
592pub struct Transaction {
593    pub date: NaiveDate,
594    pub ty: String,
595    pub payee: Option<String>,
596    pub narration: String,
597    pub tag: Option<String>,  // TODO can have multiple
598    pub link: Option<String>, // TODO can have multiple
599    pub postings: Vec<Posting>,
600    pub meta: Vec<Metadata>,
601    pub debug: DebugLine,
602    // TODO add at_cost, at_price
603}
604
605fn get_payee_narration(pairs: &mut Pairs<Rule>) -> (Option<String>, String) {
606    let first_val = pairs.next().unwrap().as_str().to_string();
607    if let Some(pair) = pairs.peek() {
608        if pair.as_rule() == Rule::narration {
609            let narration = pairs.next().unwrap().as_str().to_string();
610            return (Some(first_val), narration);
611        }
612    }
613    (None, first_val)
614}
615
616impl Transaction {
617    pub fn from_entry(entry: Pair<Rule>) -> Self {
618        let mut pairs = entry.clone().into_inner();
619        let date = pairs.next().unwrap().as_str();
620        let date = NaiveDate::parse_from_str(date, DATE_FMT).unwrap();
621        let ty = pairs.next().unwrap().as_str().to_string();
622        let (payee, narration) = get_payee_narration(&mut pairs);
623        let mut postings: Vec<Posting> = Vec::new();
624        let mut meta: Vec<Metadata> = Vec::new();
625        let mut link: Option<String> = None;
626        let mut tag: Option<String> = None;
627        for pair in pairs {
628            match pair.as_rule() {
629                Rule::posting => {
630                    postings.push(Posting::from_entry(pair));
631                }
632                Rule::metadata => {
633                    meta.push(Metadata::from_entry(pair));
634                }
635                Rule::link => {
636                    link = Some(entry.as_str().to_owned());
637                }
638                Rule::tag => {
639                    tag = Some(entry.as_str().to_owned());
640                }
641                _ => {
642                    let (line, _) = entry.line_col();
643                    let debug = DebugLine::new(line);
644                    unreachable!("Unexpected entry in Transaction, abort.\n{debug}");
645                }
646            }
647        }
648        let (line, _) = entry.line_col();
649        let debug = DebugLine { line };
650        Self {
651            date,
652            ty,
653            payee,
654            narration,
655            tag,
656            link,
657            postings,
658            meta,
659            debug,
660        }
661    }
662    pub fn from_pad(pad: Pad, amount: Amount) -> Self {
663        let date = pad.date;
664        let ty = String::from("pad");
665        let payee = None;
666        let narration = String::new();
667        let debug: DebugLine = DebugLine::default();
668        let link = None;
669        let tag = None;
670        let amount2 = Some(Amount {
671            number: -amount.clone().number,
672            ccy: amount.clone().ccy,
673        });
674        let amount = Some(amount);
675        let p1 = Posting {
676            account: pad.account_to,
677            amount: amount.clone(),
678            debug: Some(debug.clone()),
679        };
680        let p2 = Posting {
681            account: pad.account_from,
682            amount: amount2,
683            debug: Some(debug.clone()),
684        };
685        let postings = vec![p1, p2];
686        let meta: Vec<Metadata> = Vec::new();
687        Self {
688            date,
689            ty,
690            payee,
691            narration,
692            link,
693            tag,
694            postings,
695            meta,
696            debug: debug.clone(),
697        }
698    }
699}
700
701impl fmt::Display for Transaction {
702    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
703        let payee_str = match &self.payee {
704            Some(payee) => payee.as_str(),
705            None => "",
706        };
707
708        let mut posting_string = String::new();
709        let slice = &self.postings[..];
710        for p in slice {
711            let line: &str = &format!("\n{p}");
712            posting_string.push_str(line);
713        }
714
715        let mut meta_string = String::new();
716        let m_slice = &self.meta[..];
717        for m in m_slice {
718            let line: &str = &format!("\n{m}");
719            meta_string.push_str(line);
720        }
721
722        write!(
723            f,
724            "{date} {ty} {payee} {narration}{meta}{postings}",
725            date = self.date,
726            ty = self.ty,
727            payee = payee_str,
728            narration = self.narration,
729            meta = meta_string,
730            postings = posting_string,
731        )
732    }
733}
734
735/// The "ledger" is made up of Directives
736/// Most operations will be done by looping through a Vec of these
737#[derive(Clone, Debug)]
738pub enum Directive {
739    ConfigCustom(ConfigCustom),
740    Commodity(Commodity),
741    Open(Open),
742    Close(Close),
743    Balance(Balance),
744    Pad(Pad),
745    Price(Price),
746    Document(Document),
747    Note(Note),
748    Query(Query),
749    Transaction(Transaction),
750}
751
752impl Directive {
753    pub fn date(&self) -> &NaiveDate {
754        match self {
755            Directive::ConfigCustom(d) => &d.date,
756            Directive::Commodity(d) => &d.date,
757            Directive::Open(d) => &d.date,
758            Directive::Close(d) => &d.date,
759            Directive::Balance(d) => &d.date,
760            Directive::Pad(d) => &d.date,
761            Directive::Price(d) => &d.date,
762            Directive::Document(d) => &d.date,
763            Directive::Note(d) => &d.date,
764            Directive::Query(d) => &d.date,
765            Directive::Transaction(d) => &d.date,
766        }
767    }
768    /// This follows beancount's ordering logic, that always evaluates
769    /// opens -> balances -> the rest -> documents -> closes
770    pub fn order(&self) -> i8 {
771        match self {
772            Directive::Open(_) => -2,
773            Directive::Balance(_) => -1,
774            Directive::ConfigCustom(_) => 0,
775            Directive::Commodity(_) => 0,
776            Directive::Pad(_) => 0,
777            Directive::Price(_) => 0,
778            Directive::Transaction(_) => 0,
779            Directive::Document(_) => 1,
780            Directive::Note(_) => 1,
781            Directive::Query(_) => 1,
782            Directive::Close(_) => 2,
783        }
784    }
785}
786
787impl fmt::Display for Directive {
788    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
789        match self {
790            Directive::ConfigCustom(d) => write!(f, "{d}"),
791            Directive::Commodity(d) => write!(f, "{d}"),
792            Directive::Open(d) => write!(f, "{d}"),
793            Directive::Close(d) => write!(f, "{d}"),
794            Directive::Balance(d) => write!(f, "{d}"),
795            Directive::Pad(d) => write!(f, "{d}"),
796            Directive::Price(d) => write!(f, "{d}"),
797            Directive::Document(d) => write!(f, "{d}"),
798            Directive::Note(d) => write!(f, "{d}"),
799            Directive::Query(d) => write!(f, "{d}"),
800            Directive::Transaction(d) => write!(f, "{d}"),
801        }
802    }
803}
804
805#[cfg(test)]
806mod tests {
807    use super::*;
808    use crate::{ledger::Ledger, loader};
809
810    #[test]
811    fn test_open() {
812        let text = r#"2023-01-01 open Assets:Bank GBP"#;
813        let entries = loader::load(&text);
814        let Ledger {
815            dirs,
816            errs: _,
817            opts: _,
818        } = loader::consume(entries);
819        let date = NaiveDate::parse_from_str("2023-01-01", DATE_FMT).unwrap();
820        let a = &Open {
821            date,
822            account: String::from("Assets:Bank"),
823            ccys: vec!["GBP".to_owned()],
824            meta: Vec::new(),
825            debug: DebugLine { line: 2 },
826        };
827        let got = &dirs[0];
828        match got {
829            Directive::Open(i) => {
830                assert!(i == a);
831            }
832            _ => assert!(false, "Found wrong directive type"),
833        }
834    }
835
836    #[test]
837    #[should_panic]
838    fn test_bad_amount() {
839        let text = r#"
840            2023-01-01 price FOO 1,.0.0 BAR
841        "#;
842        let mut entries = loader::load(&text);
843        let entry = entries.next().unwrap();
844        Price::from_entry(entry);
845    }
846}