1use base64::prelude::*;
2use serde::de::{Deserializer, Error};
3
4use crate::value::Value;
5
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub enum CompareOp {
8 Equal,
9 NotEqual,
10 GreaterThanEqual,
11 GreaterThan,
12 LessThanEqual,
13 LessThan,
14 Is,
15 Like,
16 Regexp,
17}
18
19impl CompareOp {
20 pub fn from(qualifier: &str) -> Option<Self> {
21 return match qualifier {
22 "$eq" => Some(Self::Equal),
23 "$ne" => Some(Self::NotEqual),
24 "$gte" => Some(Self::GreaterThanEqual),
25 "$gt" => Some(Self::GreaterThan),
26 "$lte" => Some(Self::LessThanEqual),
27 "$lt" => Some(Self::LessThan),
28 "$is" => Some(Self::Is),
29 "$like" => Some(Self::Like),
30 "$re" => Some(Self::Regexp),
31 _ => None,
32 };
33 }
34
35 #[inline]
36 pub fn as_sql(&self) -> &'static str {
37 return match self {
38 Self::GreaterThanEqual => ">=",
39 Self::GreaterThan => ">",
40 Self::LessThanEqual => "<=",
41 Self::LessThan => "<",
42 Self::NotEqual => "<>",
43 Self::Is => "IS",
44 Self::Like => "LIKE",
45 Self::Regexp => "REGEXP",
46 Self::Equal => "=",
47 };
48 }
49
50 #[inline]
51 pub fn as_query(&self) -> &'static str {
52 return match self {
53 Self::Equal => "$eq",
54 Self::NotEqual => "$ne",
55 Self::GreaterThanEqual => "$gte",
56 Self::GreaterThan => "$gt",
57 Self::LessThanEqual => "$lte",
58 Self::LessThan => "$lt",
59 Self::Is => "$is",
60 Self::Like => "$like",
61 Self::Regexp => "$re",
62 };
63 }
64}
65
66#[derive(Clone, Debug, PartialEq)]
68pub struct ColumnOpValue {
69 pub column: String,
70 pub op: CompareOp,
71 pub value: Value,
72}
73
74fn parse_value<'de, D>(op: CompareOp, value: serde_value::Value) -> Result<Value, D::Error>
75where
76 D: Deserializer<'de>,
77{
78 use crate::util::unexpected;
79
80 return match op {
81 CompareOp::Is => match value {
82 serde_value::Value::String(value) if value == "NULL" => Ok(Value::String("NULL".to_string())),
83 serde_value::Value::String(value) if value == "!NULL" => {
84 Ok(Value::String("NOT NULL".to_string()))
85 }
86 _ => Err(Error::invalid_type(unexpected(&value), &"NULL or !NULL")),
87 },
88 _ => match value {
89 serde_value::Value::String(value) => Ok(Value::unparse(value)),
90 serde_value::Value::Bytes(bytes) => Ok(Value::String(BASE64_URL_SAFE.encode(bytes))),
91 serde_value::Value::I64(i) => Ok(Value::Integer(i)),
92 serde_value::Value::I32(i) => Ok(Value::Integer(i as i64)),
93 serde_value::Value::I16(i) => Ok(Value::Integer(i as i64)),
94 serde_value::Value::I8(i) => Ok(Value::Integer(i as i64)),
95 serde_value::Value::U64(i) => Ok(Value::Integer(i as i64)),
96 serde_value::Value::U32(i) => Ok(Value::Integer(i as i64)),
97 serde_value::Value::U16(i) => Ok(Value::Integer(i as i64)),
98 serde_value::Value::U8(i) => Ok(Value::Integer(i as i64)),
99 serde_value::Value::Bool(b) => Ok(Value::Integer(if b { 1 } else { 0 })),
100 _ => Err(Error::invalid_type(
101 unexpected(&value),
102 &"trailbase_qs::Value, i.e. string, integer, double or bool",
103 )),
104 },
105 };
106}
107
108pub fn serde_value_to_single_column_rel_value<'de, D>(
109 key: String,
110 value: serde_value::Value,
111) -> Result<ColumnOpValue, D::Error>
112where
113 D: Deserializer<'de>,
114{
115 use crate::util::unexpected;
116 use serde_value::Value;
117
118 if !crate::util::sanitize_column_name(&key) {
119 return Err(Error::custom(format!(
122 "invalid column name for filter: {key}. Nesting too deep?"
123 )));
124 }
125
126 return match value {
127 Value::String(_) => Ok(ColumnOpValue {
129 column: key,
130 op: CompareOp::Equal,
131 value: parse_value::<D>(CompareOp::Equal, value)?,
132 }),
133 Value::Map(mut m) if m.len() == 1 => {
135 let (k, v) = m.pop_first().expect("len() == 1");
136
137 let op = if let Value::String(ref op_str) = k {
138 CompareOp::from(op_str).ok_or_else(|| Error::invalid_type(unexpected(&k), &OP_ERR))?
139 } else {
140 return Err(Error::invalid_type(unexpected(&k), &OP_ERR));
141 };
142
143 Ok(ColumnOpValue {
144 column: key,
145 value: parse_value::<D>(op, v)?,
146 op,
147 })
148 }
149 v => Err(Error::invalid_type(
150 unexpected(&v),
151 &"[column_name]=value or [column_name][$op]=value",
152 )),
153 };
154}
155
156const OP_ERR: &str = "one of [$eq, $ne, $lt, ...]";