tackler_api/filters/posting/
posting_amount_greater.rs1use jiff::tz::TimeZone;
7use regex::Regex;
8use rust_decimal::Decimal;
9use serde::{Deserialize, Serialize};
10use std::fmt::Formatter;
11use tackler_rs::regex::peeled_pattern;
12use tackler_rs::regex::serde::full_haystack_matcher;
13
14use crate::filters::{IndentDisplay, posting_filter_indent_fmt};
15
16#[derive(Serialize, Deserialize, Clone, Debug)]
30pub struct TxnFilterPostingAmountGreater {
31 #[doc(hidden)]
32 #[serde(with = "full_haystack_matcher")]
33 pub regex: Regex,
34 #[doc(hidden)]
35 #[serde(with = "rust_decimal::serde::arbitrary_precision")]
36 pub amount: Decimal,
37}
38
39impl IndentDisplay for TxnFilterPostingAmountGreater {
40 fn i_fmt(&self, indent: &str, _tz: TimeZone, f: &mut Formatter<'_>) -> std::fmt::Result {
41 posting_filter_indent_fmt(
42 indent,
43 "Posting Amount",
44 peeled_pattern(&self.regex),
45 ">",
46 &self.amount,
47 f,
48 )
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::filters::{
56 FilterDefZoned, FilterDefinition, NullaryTRUE, TxnFilter, logic::TxnFilterAND,
57 };
58 use indoc::indoc;
59 use jiff::tz;
60 use tackler_rs::IndocUtils;
61 use tackler_rs::regex::new_full_haystack_regex;
62
63 #[test]
64 fn posting_amount_greater_full_haystack() {
67 let filter_json_str =
68 r#"{"txnFilter":{"TxnFilterPostingAmountGreater":{"regex":"o.a","amount":1}}}"#;
69
70 let tf_res = serde_json::from_str::<FilterDefinition>(filter_json_str);
71 assert!(tf_res.is_ok());
72 let tf = tf_res.unwrap();
73
74 if let TxnFilter::TxnFilterPostingAmountGreater(f) = &tf.txn_filter {
75 assert!(!f.regex.is_match("foobar"));
76 assert!(!f.regex.is_match("obar"));
77 assert!(!f.regex.is_match("ooba"));
78
79 assert!(f.regex.is_match("oba"));
80 } else {
81 panic!()
82 }
83 }
84
85 #[test]
86 fn posting_amount_greater_json() {
89 let filter_json_str = r#"{"txnFilter":{"TxnFilterPostingAmountGreater":{"regex":"(abc.*)|(def.*)","amount":1}}}"#;
90
91 let filter_text_str = indoc! {
92 r#"|Filter
93 | Posting Amount
94 | account: "(abc.*)|(def.*)"
95 | amount > 1
96 |"#}
97 .strip_margin();
98
99 let tf_res = serde_json::from_str::<FilterDefinition>(filter_json_str);
100 assert!(tf_res.is_ok());
101 let tf = tf_res.unwrap();
102
103 if let TxnFilter::TxnFilterPostingAmountGreater(_) = tf.txn_filter {
104 } else {
105 panic!()
106 }
107
108 assert_eq!(
109 format!(
110 "{}",
111 FilterDefZoned {
112 filt_def: &tf,
113 tz: tz::TimeZone::UTC
114 }
115 ),
116 filter_text_str
117 );
118 assert_eq!(
119 serde_json::to_string(&tf).unwrap(),
120 filter_json_str
121 );
122 }
123
124 #[test]
125 fn posting_amount_greater_text() {
128 let filter_text_str = indoc! {
129 r#"|Filter
130 | AND
131 | Posting Amount
132 | account: "(abc.*)|(def.*)"
133 | amount > 1
134 | AND
135 | Posting Amount
136 | account: "xyz"
137 | amount > 2
138 | All pass
139 |"#}
140 .strip_margin();
141
142 let tf = FilterDefinition {
143 txn_filter: TxnFilter::TxnFilterAND(TxnFilterAND {
144 txn_filters: vec![
145 TxnFilter::TxnFilterPostingAmountGreater(TxnFilterPostingAmountGreater {
146 regex: new_full_haystack_regex("(abc.*)|(def.*)").unwrap(),
147 amount: Decimal::from(1),
148 }),
149 TxnFilter::TxnFilterAND(TxnFilterAND {
150 txn_filters: vec![
151 TxnFilter::TxnFilterPostingAmountGreater(
152 TxnFilterPostingAmountGreater {
153 regex: new_full_haystack_regex("xyz").unwrap(),
154 amount: Decimal::from(2),
155 },
156 ),
157 TxnFilter::NullaryTRUE(NullaryTRUE {}),
158 ],
159 }),
160 ],
161 }),
162 };
163
164 assert_eq!(
165 format!(
166 "{}",
167 FilterDefZoned {
168 filt_def: &tf,
169 tz: tz::TimeZone::UTC
170 }
171 ),
172 filter_text_str
173 );
174 }
175}