1use crate::expressions::Expression;
9use serde_json::{json, Value};
10
11pub fn expression_from_str(input: &str) -> Result<Expression, String> {
14 let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
15 expression_from_value(value)
16}
17
18pub fn expressions_from_str(input: &str) -> Result<Vec<Expression>, String> {
21 let value: Value = serde_json::from_str(input).map_err(|error| error.to_string())?;
22 expressions_from_value(value)
23}
24
25pub fn expression_from_value(value: Value) -> Result<Expression, String> {
27 match serde_json::from_value::<Expression>(value.clone()) {
28 Ok(expression) => Ok(expression),
29 Err(original_error) => {
30 let mut repaired = value;
31 normalize_expression_value(&mut repaired);
32 serde_json::from_value::<Expression>(repaired)
33 .map_err(|error| format!("{error}; original error: {original_error}"))
34 }
35 }
36}
37
38pub fn expressions_from_value(value: Value) -> Result<Vec<Expression>, String> {
40 match serde_json::from_value::<Vec<Expression>>(value.clone()) {
41 Ok(expressions) => Ok(expressions),
42 Err(original_error) => {
43 let mut repaired = value;
44 if let Value::Array(items) = &mut repaired {
45 for item in items {
46 normalize_expression_value(item);
47 }
48 }
49 serde_json::from_value::<Vec<Expression>>(repaired)
50 .map_err(|error| format!("{error}; original error: {original_error}"))
51 }
52 }
53}
54
55pub fn expression_to_value(expression: &Expression) -> Result<Value, String> {
58 serde_json::to_value(expression).map_err(|error| error.to_string())
59}
60
61fn normalize_expression_value(value: &mut Value) {
62 match value {
63 Value::Object(map) if map.is_empty() => {
64 *value = json!({ "null": null });
65 }
66 Value::Object(map) if map.len() == 1 => {
67 let (kind, payload) = map.iter_mut().next().expect("single entry exists");
68 if kind == "null" && matches!(payload, Value::Object(inner) if inner.is_empty()) {
69 *payload = Value::Null;
70 return;
71 }
72
73 if kind == "is_null" {
74 normalize_is_null_payload(payload);
75 }
76
77 match payload {
78 Value::Object(payload_map) => normalize_struct_fields(payload_map),
79 Value::Array(items) => {
80 for item in items {
81 normalize_expression_value(item);
82 }
83 }
84 _ => {}
85 }
86 }
87 Value::Object(map) => normalize_struct_fields(map),
88 Value::Array(items) => {
89 for item in items {
90 normalize_expression_value(item);
91 }
92 }
93 _ => {}
94 }
95}
96
97fn normalize_is_null_payload(payload: &mut Value) {
98 let Value::Array(items) = payload else {
99 return;
100 };
101 if items.len() != 1 {
102 return;
103 }
104
105 let mut this = items.remove(0);
106 normalize_expression_value(&mut this);
107 *payload = json!({
108 "this": this,
109 "not": false,
110 "postfix_form": false
111 });
112}
113
114fn normalize_struct_fields(map: &mut serde_json::Map<String, Value>) {
115 for (key, value) in map.iter_mut() {
116 if is_expression_array_field(key) {
117 if let Value::Array(items) = value {
118 for item in items {
119 normalize_expression_value(item);
120 }
121 continue;
122 }
123 }
124
125 if is_expression_field(key) {
126 normalize_expression_value(value);
127 } else {
128 normalize_container_value(value);
129 }
130 }
131}
132
133fn normalize_container_value(value: &mut Value) {
134 match value {
135 Value::Object(map) => normalize_struct_fields(map),
136 Value::Array(items) => {
137 for item in items {
138 normalize_container_value(item);
139 }
140 }
141 _ => {}
142 }
143}
144
145fn is_expression_field(key: &str) -> bool {
146 matches!(
147 key,
148 "this"
149 | "left"
150 | "right"
151 | "low"
152 | "high"
153 | "expression"
154 | "condition"
155 | "query"
156 | "unnest"
157 | "on"
158 | "prewhere"
159 | "where"
160 | "where_clause"
161 | "having"
162 | "qualify"
163 | "source"
164 | "body"
165 | "default"
166 | "true_value"
167 | "false_value"
168 | "else_result"
169 | "target"
170 | "format"
171 | "limit"
172 | "offset"
173 )
174}
175
176fn is_expression_array_field(key: &str) -> bool {
177 matches!(
178 key,
179 "expressions"
180 | "args"
181 | "columns"
182 | "values"
183 | "partition_by"
184 | "order_by"
185 | "group_by"
186 | "distinct_on"
187 | "limit_by"
188 | "settings"
189 | "for_xml"
190 | "for_json"
191 | "exclude"
192 | "hints"
193 | "on_columns"
194 )
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use crate::expressions::Expression;
201
202 fn column_json(name: &str) -> Value {
203 json!({
204 "column": {
205 "name": {
206 "name": name,
207 "quoted": false,
208 "trailing_comments": [],
209 "span": null
210 },
211 "table": null,
212 "join_mark": false,
213 "trailing_comments": [],
214 "span": null,
215 "inferred_type": null
216 }
217 })
218 }
219
220 #[test]
221 fn expression_from_value_accepts_legacy_empty_object_as_null() {
222 assert!(matches!(
223 expression_from_value(json!({})).expect("empty object should become NULL"),
224 Expression::Null(_)
225 ));
226 }
227
228 #[test]
229 fn expression_from_value_accepts_empty_null_payload() {
230 assert!(matches!(
231 expression_from_value(json!({ "null": {} })).expect("empty null payload should work"),
232 Expression::Null(_)
233 ));
234 }
235
236 #[test]
237 fn expression_from_value_repairs_nested_null_expression_fields() {
238 let expression = expression_from_value(json!({
239 "between": {
240 "this": column_json("created_at"),
241 "low": { "literal": { "literal_type": "string", "value": "2024-01-01" } },
242 "high": {},
243 "not": false,
244 "symmetric": null
245 }
246 }))
247 .expect("between high bound should be repaired");
248
249 match expression {
250 Expression::Between(between) => assert!(matches!(between.high, Expression::Null(_))),
251 other => panic!("expected between expression, got {other:?}"),
252 }
253 }
254
255 #[test]
256 fn expression_from_value_accepts_is_null_array_shorthand() {
257 let expression = expression_from_value(json!({
258 "is_null": [column_json("deleted_at")]
259 }))
260 .expect("is_null shorthand should be repaired");
261
262 match expression {
263 Expression::IsNull(is_null) => {
264 assert!(!is_null.not);
265 assert!(!is_null.postfix_form);
266 assert!(matches!(is_null.this, Expression::Column(_)));
267 }
268 other => panic!("expected is_null expression, got {other:?}"),
269 }
270 }
271
272 #[test]
273 fn expression_from_value_keeps_unknown_ast_as_error() {
274 let error = expression_from_value(json!({ "not_a_real_expression": {} }))
275 .expect_err("unknown expression should stay invalid");
276 assert!(error.contains("not_a_real_expression"));
277 }
278}