1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum UnOper {
15 Not,
17}
18
19impl UnOper {
20 #[must_use]
22 pub fn as_str(&self) -> &'static str {
23 match self {
24 Self::Not => "NOT",
25 }
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum BinOper {
34 And,
37 Or,
39
40 Equal,
43 NotEqual,
45 SmallerThan,
47 SmallerThanOrEqual,
49 GreaterThan,
51 GreaterThanOrEqual,
53
54 Like,
57 NotLike,
59 ILike,
61 NotILike,
63 SimilarTo,
65 NotSimilarTo,
67 Matches,
69 NotMatches,
71
72 In,
75 NotIn,
77 Between,
79 NotBetween,
81
82 Is,
85 IsNot,
87
88 Add,
91 Sub,
93 Mul,
95 Div,
97 Mod,
99
100 BitAnd,
103 BitOr,
105 LShift,
107 RShift,
109
110 PgOperator(PgBinOper),
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117pub enum PgBinOper {
118 Contains,
120 Contained,
122 Overlap,
124 Concatenate,
126 JsonContainsKey,
128 JsonContainsAnyKey,
130 JsonContainsAllKeys,
132 JsonGetByIndex,
134 JsonGetAsText,
136 JsonGetPath,
138 JsonGetPathAsText,
140}
141
142impl BinOper {
143 #[must_use]
145 pub fn as_str(&self) -> &'static str {
146 match self {
147 Self::And => "AND",
148 Self::Or => "OR",
149 Self::Equal => "=",
150 Self::NotEqual => "<>",
151 Self::SmallerThan => "<",
152 Self::SmallerThanOrEqual => "<=",
153 Self::GreaterThan => ">",
154 Self::GreaterThanOrEqual => ">=",
155 Self::Like => "LIKE",
156 Self::NotLike => "NOT LIKE",
157 Self::ILike => "ILIKE",
158 Self::NotILike => "NOT ILIKE",
159 Self::SimilarTo => "SIMILAR TO",
160 Self::NotSimilarTo => "NOT SIMILAR TO",
161 Self::Matches => "~",
162 Self::NotMatches => "!~",
163 Self::In => "IN",
164 Self::NotIn => "NOT IN",
165 Self::Between => "BETWEEN",
166 Self::NotBetween => "NOT BETWEEN",
167 Self::Is => "IS",
168 Self::IsNot => "IS NOT",
169 Self::Add => "+",
170 Self::Sub => "-",
171 Self::Mul => "*",
172 Self::Div => "/",
173 Self::Mod => "%",
174 Self::BitAnd => "&",
175 Self::BitOr => "|",
176 Self::LShift => "<<",
177 Self::RShift => ">>",
178 Self::PgOperator(pg_op) => pg_op.as_str(),
179 }
180 }
181
182 #[must_use]
186 pub fn precedence(&self) -> u8 {
187 match self {
188 Self::Or => 1,
189 Self::And => 2,
190 Self::Is | Self::IsNot => 3,
191 Self::Between | Self::NotBetween | Self::In | Self::NotIn => 4,
192 Self::Like
193 | Self::NotLike
194 | Self::ILike
195 | Self::NotILike
196 | Self::SimilarTo
197 | Self::NotSimilarTo
198 | Self::Matches
199 | Self::NotMatches => 5,
200 Self::Equal
201 | Self::NotEqual
202 | Self::SmallerThan
203 | Self::SmallerThanOrEqual
204 | Self::GreaterThan
205 | Self::GreaterThanOrEqual => 6,
206 Self::BitOr => 7,
207 Self::BitAnd => 8,
208 Self::LShift | Self::RShift => 9,
209 Self::Add | Self::Sub => 10,
210 Self::Mul | Self::Div | Self::Mod => 11,
211 Self::PgOperator(_) => 6, }
213 }
214
215 #[must_use]
217 pub fn is_left_associative(&self) -> bool {
218 true
220 }
221}
222
223impl PgBinOper {
224 #[must_use]
226 pub fn as_str(&self) -> &'static str {
227 match self {
228 Self::Contains => "@>",
229 Self::Contained => "<@",
230 Self::Overlap => "&&",
231 Self::Concatenate => "||",
232 Self::JsonContainsKey => "?",
233 Self::JsonContainsAnyKey => "?|",
234 Self::JsonContainsAllKeys => "?&",
235 Self::JsonGetByIndex => "->",
236 Self::JsonGetAsText => "->>",
237 Self::JsonGetPath => "#>",
238 Self::JsonGetPathAsText => "#>>",
239 }
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
247pub enum LogicalChainOper {
248 And,
250 Or,
252}
253
254impl LogicalChainOper {
255 #[must_use]
257 pub fn as_str(&self) -> &'static str {
258 match self {
259 Self::And => "AND",
260 Self::Or => "OR",
261 }
262 }
263}
264
265impl From<LogicalChainOper> for BinOper {
266 fn from(op: LogicalChainOper) -> Self {
267 match op {
268 LogicalChainOper::And => BinOper::And,
269 LogicalChainOper::Or => BinOper::Or,
270 }
271 }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
278pub enum SubQueryOper {
279 Exists,
281 Any,
283 Some,
285 All,
287}
288
289impl SubQueryOper {
290 #[must_use]
292 pub fn as_str(&self) -> &'static str {
293 match self {
294 Self::Exists => "EXISTS",
295 Self::Any => "ANY",
296 Self::Some => "SOME",
297 Self::All => "ALL",
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305 use rstest::rstest;
306
307 #[rstest]
308 fn test_un_oper_as_str() {
309 assert_eq!(UnOper::Not.as_str(), "NOT");
310 }
311
312 #[rstest]
313 #[case(BinOper::And, "AND")]
314 #[case(BinOper::Or, "OR")]
315 #[case(BinOper::Equal, "=")]
316 #[case(BinOper::NotEqual, "<>")]
317 #[case(BinOper::SmallerThan, "<")]
318 #[case(BinOper::GreaterThan, ">")]
319 #[case(BinOper::Like, "LIKE")]
320 #[case(BinOper::In, "IN")]
321 #[case(BinOper::Between, "BETWEEN")]
322 fn test_bin_oper_as_str(#[case] op: BinOper, #[case] expected: &str) {
323 assert_eq!(op.as_str(), expected);
324 }
325
326 #[rstest]
327 fn test_bin_oper_precedence() {
328 assert!(BinOper::Mul.precedence() > BinOper::Add.precedence());
330 assert!(BinOper::And.precedence() > BinOper::Or.precedence());
332 assert!(BinOper::Equal.precedence() > BinOper::And.precedence());
334 }
335
336 #[rstest]
337 fn test_logical_chain_oper() {
338 assert_eq!(LogicalChainOper::And.as_str(), "AND");
339 assert_eq!(LogicalChainOper::Or.as_str(), "OR");
340 }
341
342 #[rstest]
343 fn test_subquery_oper() {
344 assert_eq!(SubQueryOper::Exists.as_str(), "EXISTS");
345 assert_eq!(SubQueryOper::Any.as_str(), "ANY");
346 assert_eq!(SubQueryOper::All.as_str(), "ALL");
347 }
348
349 #[rstest]
350 fn test_pg_bin_oper() {
351 assert_eq!(PgBinOper::Contains.as_str(), "@>");
352 assert_eq!(PgBinOper::Contained.as_str(), "<@");
353 assert_eq!(PgBinOper::JsonGetAsText.as_str(), "->>");
354 }
355}