layered_amount/
amounts.rs1use layered_nlp::{x, LLCursorAssignment, LLSelection, Resolver, TextTag};
2use rust_decimal::Decimal;
3
4#[derive(Clone, Debug)]
5pub struct Amount(Decimal);
6
7impl Amount {
8 pub fn get_decimal(&self) -> &Decimal {
9 &self.0
10 }
11 pub fn new<T: Into<Decimal>>(from: T) -> Self {
12 Self(from.into())
13 }
14}
15
16impl From<Decimal> for Amount {
17 fn from(dec: Decimal) -> Self {
18 Amount(dec)
19 }
20}
21
22pub struct AmountResolver {
23 delimiters: Vec<char>,
25 decimal: char,
26}
27
28impl AmountResolver {
29 pub fn new(delimiters: Vec<char>, decimal: char) -> Self {
30 Self {
31 delimiters,
32 decimal,
33 }
34 }
35 pub fn english() -> Self {
41 Self {
42 delimiters: vec![',', '\''],
43 decimal: '.',
44 }
45 }
46 pub fn french() -> Self {
52 Self {
53 delimiters: vec![' ', '.', '\''],
54 decimal: '.',
55 }
56 }
57}
58
59impl Resolver for AmountResolver {
60 type Attr = Amount;
61
62 fn go(&self, mut search_range_sel: LLSelection) -> Vec<LLCursorAssignment<Self::Attr>> {
63 let mut attrs = vec![];
64
65 while let Some((mut selection, (_, text))) =
66 search_range_sel.find_first_by(&x::all((x::attr_eq(&TextTag::NATN), x::token_text())))
67 {
68 let mut number_string = String::from(text);
69 let mut last_valid_selection = None;
70
71 loop {
73 if let Some((delimeter_sel, _)) =
74 selection.match_first_forwards(&x::token_has_any(self.delimiters.as_slice()))
75 {
76 last_valid_selection = Some(selection);
77 selection = delimeter_sel;
78 } else {
79 break;
80 }
81
82 if let Some((following_delimeter_sel, (_, text))) = selection
83 .match_first_forwards(&x::all((x::attr_eq(&TextTag::NATN), x::token_text())))
84 {
85 number_string.push_str(text);
86 last_valid_selection = None;
87 selection = following_delimeter_sel;
88 } else {
89 break;
90 }
91 }
92
93 if last_valid_selection.is_none() {
94 if let Some((with_decimal_sel, _)) =
96 selection.match_first_forwards(&x::token_has_any(&[self.decimal]))
97 {
98 last_valid_selection = Some(selection);
99 number_string.push('.');
100 selection = with_decimal_sel;
101 }
102
103 if let Some((following_decimal_sel, ((), text))) = selection
104 .match_first_forwards(&x::all((x::attr_eq(&TextTag::NATN), x::token_text())))
105 {
106 number_string.push_str(text);
107 last_valid_selection = None;
108 selection = following_decimal_sel;
109 }
110 }
111
112 attrs.push(
113 last_valid_selection
114 .unwrap_or_else(|| selection.clone())
115 .finish_with_attr(Amount(number_string.parse::<Decimal>().unwrap())),
116 );
117
118 if let [_, Some(right_sel)] = search_range_sel.split_with(&selection) {
119 search_range_sel = right_sel;
120 } else {
121 break;
122 }
123 }
124
125 attrs
126 }
127}