satex_core/
expression.rs

1use crate::Error;
2use regex::Regex;
3use serde::de::Error as DeError;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt::{Display, Formatter, Write};
6use std::str::FromStr;
7use std::sync::Arc;
8
9#[derive(Debug, Clone)]
10pub enum Expression {
11    Equals(Value),
12    NotEquals(Value),
13    StartsWith(Value),
14    NotStartsWith(Value),
15    EndsWith(Value),
16    NotEndsWith(Value),
17    Contains(Value),
18    NotContains(Value),
19    Exists,
20    NotExists,
21    Regex(Regex),
22}
23
24impl Expression {
25    pub fn equals(value: impl AsRef<str>, sensitive: bool) -> Self {
26        Expression::Equals(Value::new(value, sensitive))
27    }
28
29    pub fn not_equals(value: impl AsRef<str>, sensitive: bool) -> Self {
30        Expression::NotEquals(Value::new(value, sensitive))
31    }
32
33    pub fn starts_with(value: impl AsRef<str>, sensitive: bool) -> Self {
34        Expression::StartsWith(Value::new(value, sensitive))
35    }
36
37    pub fn not_starts_with(value: impl AsRef<str>, sensitive: bool) -> Self {
38        Expression::NotStartsWith(Value::new(value, sensitive))
39    }
40
41    pub fn ends_with(value: impl AsRef<str>, sensitive: bool) -> Self {
42        Expression::EndsWith(Value::new(value, sensitive))
43    }
44
45    pub fn not_ends_with(value: impl AsRef<str>, sensitive: bool) -> Self {
46        Expression::NotEndsWith(Value::new(value, sensitive))
47    }
48
49    pub fn contains(value: impl AsRef<str>, sensitive: bool) -> Self {
50        Expression::Contains(Value::new(value, sensitive))
51    }
52
53    pub fn not_contains(value: impl AsRef<str>, sensitive: bool) -> Self {
54        Expression::NotContains(Value::new(value, sensitive))
55    }
56
57    pub fn regex(value: impl AsRef<str>) -> Result<Self, Error> {
58        Regex::from_str(value.as_ref())
59            .map(Expression::Regex)
60            .map_err(Error::new)
61    }
62
63    pub fn exists() -> Self {
64        Self::Exists
65    }
66
67    pub fn not_exists() -> Self {
68        Self::NotExists
69    }
70
71    pub fn matches(&self, text: Option<&str>) -> bool {
72        match self {
73            Expression::Equals(value) => value.equals(text),
74            Expression::NotEquals(value) => value.not_equals(text),
75            Expression::StartsWith(value) => value.starts_with(text),
76            Expression::NotStartsWith(value) => value.not_starts_with(text),
77            Expression::EndsWith(value) => value.ends_with(text),
78            Expression::NotEndsWith(value) => value.not_ends_with(text),
79            Expression::Contains(value) => value.contains(text),
80            Expression::NotContains(value) => value.not_contains(text),
81            Expression::Exists => text.is_some(),
82            Expression::NotExists => text.is_none(),
83            Expression::Regex(regex) => match text {
84                Some(text) => regex.is_match(text),
85                None => false,
86            },
87        }
88    }
89}
90
91impl Display for Expression {
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        match self {
94            Expression::Equals(value) => {
95                f.write_str("Equals")?;
96                <Value as Display>::fmt(value, f)
97            }
98            Expression::NotEquals(value) => {
99                f.write_str("NotEquals")?;
100                <Value as Display>::fmt(value, f)
101            }
102            Expression::StartsWith(value) => {
103                f.write_str("StartsWith")?;
104                <Value as Display>::fmt(value, f)
105            }
106            Expression::NotStartsWith(value) => {
107                f.write_str("NotStartWith")?;
108                <Value as Display>::fmt(value, f)
109            }
110            Expression::EndsWith(value) => {
111                f.write_str("EndsWith")?;
112                <Value as Display>::fmt(value, f)
113            }
114            Expression::NotEndsWith(value) => {
115                f.write_str("NotEndsWith")?;
116                <Value as Display>::fmt(value, f)
117            }
118            Expression::Contains(value) => {
119                f.write_str("Contains")?;
120                <Value as Display>::fmt(value, f)
121            }
122            Expression::NotContains(value) => {
123                f.write_str("NotContains")?;
124                <Value as Display>::fmt(value, f)
125            }
126            Expression::Exists => f.write_str("Exists"),
127            Expression::NotExists => f.write_str("NotExists"),
128            Expression::Regex(regex) => f.write_str(regex.as_str()),
129        }
130    }
131}
132
133impl FromStr for Expression {
134    type Err = Error;
135
136    fn from_str(value: &str) -> Result<Self, Self::Err> {
137        let value = value.trim();
138        match value {
139            "Exists" => Ok(Expression::Exists),
140            "NotExists" => Ok(Expression::NotExists),
141            _ => match value.find('(').zip(value.rfind(')')) {
142                Some((open, close)) => {
143                    let r#type = value[..open].trim();
144                    let value = &value[open + 1..close];
145                    match r#type {
146                        "Equals" => Ok(Expression::equals(value, true)),
147                        "?Equals" => Ok(Expression::equals(value, false)),
148                        "NotEquals" => Ok(Expression::not_equals(value, true)),
149                        "?NotEquals" => Ok(Expression::not_equals(value, false)),
150                        "StartsWith" => Ok(Expression::starts_with(value, true)),
151                        "?StartsWith" => Ok(Expression::starts_with(value, false)),
152                        "NotStartsWith" => Ok(Expression::not_starts_with(value, true)),
153                        "?NotStartsWith" => Ok(Expression::not_starts_with(value, false)),
154                        "EndsWith" => Ok(Expression::ends_with(value, true)),
155                        "?EndsWith" => Ok(Expression::ends_with(value, false)),
156                        "NotEndsWith" => Ok(Expression::not_ends_with(value, true)),
157                        "?NotEndsWith" => Ok(Expression::not_ends_with(value, false)),
158                        "Contains" => Ok(Expression::contains(value, true)),
159                        "?Contains" => Ok(Expression::contains(value, false)),
160                        "NotContains" => Ok(Expression::not_contains(value, true)),
161                        "?NotContains" => Ok(Expression::not_contains(value, false)),
162                        "Regex" => Expression::regex(value),
163                        _ => Err(Error::new(format!("invalid expression type: {}", r#type))),
164                    }
165                }
166                None => Err(Error::new(format!("invalid expression: {}", value))),
167            },
168        }
169    }
170}
171
172impl<'de> Deserialize<'de> for Expression {
173    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174    where
175        D: Deserializer<'de>,
176    {
177        let text = String::deserialize(deserializer)?;
178        Expression::from_str(&text).map_err(D::Error::custom)
179    }
180}
181
182impl Serialize for Expression {
183    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184    where
185        S: Serializer,
186    {
187        self.to_string().serialize(serializer)
188    }
189}
190
191#[derive(Debug, Clone)]
192pub struct Value {
193    raw: Arc<str>,
194    sensitive: bool,
195}
196
197impl Display for Value {
198    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199        let (value, enabled) = match self.sensitive {
200            true => (self.raw.as_ref(), true),
201            false => (self.raw.as_ref(), false),
202        };
203        f.write_char('(')?;
204        f.write_str(value)?;
205        f.write_char(',')?;
206        f.write_str(if enabled { "true" } else { "false" })?;
207        f.write_char(')')
208    }
209}
210
211impl Value {
212    pub fn new(value: impl AsRef<str>, sensitive: bool) -> Self {
213        let raw = if sensitive {
214            Arc::from(value.as_ref())
215        } else {
216            Arc::from(value.as_ref().to_lowercase())
217        };
218        Self { raw, sensitive }
219    }
220
221    pub fn equals(&self, value: Option<&str>) -> bool {
222        match value {
223            Some(value) => {
224                if self.sensitive {
225                    self.raw.as_ref() == value
226                } else {
227                    self.raw.as_ref().eq_ignore_ascii_case(value)
228                }
229            }
230            None => false,
231        }
232    }
233
234    pub fn not_equals(&self, value: Option<&str>) -> bool {
235        !self.equals(value)
236    }
237
238    pub fn starts_with(&self, value: Option<&str>) -> bool {
239        match value {
240            Some(value) => {
241                if self.sensitive {
242                    value.starts_with(self.raw.as_ref())
243                } else {
244                    value.to_lowercase().starts_with(self.raw.as_ref())
245                }
246            }
247            None => false,
248        }
249    }
250
251    pub fn not_starts_with(&self, value: Option<&str>) -> bool {
252        !self.starts_with(value)
253    }
254
255    pub fn ends_with(&self, value: Option<&str>) -> bool {
256        match value {
257            Some(value) => {
258                if self.sensitive {
259                    value.ends_with(self.raw.as_ref())
260                } else {
261                    value.to_lowercase().ends_with(self.raw.as_ref())
262                }
263            }
264            None => false,
265        }
266    }
267
268    pub fn not_ends_with(&self, value: Option<&str>) -> bool {
269        !self.ends_with(value)
270    }
271
272    pub fn contains(&self, value: Option<&str>) -> bool {
273        match value {
274            Some(value) => {
275                if self.sensitive {
276                    value.contains(self.raw.as_ref())
277                } else {
278                    value.to_lowercase().contains(self.raw.as_ref())
279                }
280            }
281            None => false,
282        }
283    }
284
285    pub fn not_contains(&self, value: Option<&str>) -> bool {
286        !self.contains(value)
287    }
288}