Skip to main content

reifydb_rql/expression/
json.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! JSON serialization for Expression types.
5//!
6//! This module provides clean JSON serialization that:
7//! - Skips Fragment source location metadata (line/column positions)
8//! - Preserves semantic data only (values, types, names)
9//! - Supports round-trip serialization/deserialization
10//! - Is suitable for frontend query builders
11
12use std::sync::Arc;
13
14use reifydb_core::{
15	interface::identifier::{ColumnIdentifier, ColumnPrimitive},
16	internal,
17};
18use reifydb_type::{
19	Result,
20	error::{Error, TypeError},
21	fragment::Fragment,
22	value::r#type::Type,
23};
24use serde::{Deserialize, Serialize};
25use serde_json::{from_str, to_string, to_string_pretty};
26
27use super::{
28	AccessPrimitiveExpression, AddExpression, AliasExpression, AndExpression, BetweenExpression, CallExpression,
29	CastExpression, ColumnExpression, ConstantExpression, ContainsExpression, DivExpression, ElseIfExpression,
30	EqExpression, Expression, ExtendExpression, FieldAccessExpression, GreaterThanEqExpression,
31	GreaterThanExpression, IdentExpression, IfExpression, InExpression, LessThanEqExpression, LessThanExpression,
32	ListExpression, MapExpression, MulExpression, NotEqExpression, OrExpression, ParameterExpression,
33	PrefixExpression, PrefixOperator, RemExpression, SubExpression, TupleExpression, TypeExpression,
34	VariableExpression, XorExpression,
35};
36
37/// JSON-serializable expression for query builders.
38///
39/// This enum mirrors the `Expression` type but uses simple owned types
40/// instead of lifetimed `Fragment`s, making it suitable for JSON serialization.
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(tag = "type", rename_all = "snake_case")]
43pub enum JsonExpression {
44	// Constants
45	None,
46	Bool {
47		value: String,
48	},
49	Number {
50		value: String,
51	},
52	Text {
53		value: String,
54	},
55	Temporal {
56		value: String,
57	},
58
59	// Identifiers
60	Column {
61		namespace: String,
62		source: String,
63		name: String,
64	},
65	AccessSource {
66		namespace: String,
67		source: String,
68		name: String,
69	},
70	Variable {
71		name: String,
72	},
73	#[serde(rename = "parameter_positional")]
74	ParameterPositional {
75		position: String,
76	},
77	#[serde(rename = "parameter_named")]
78	ParameterNamed {
79		name: String,
80	},
81
82	// Comparison
83	GreaterThan {
84		left: Box<JsonExpression>,
85		right: Box<JsonExpression>,
86	},
87	GreaterThanEqual {
88		left: Box<JsonExpression>,
89		right: Box<JsonExpression>,
90	},
91	LessThan {
92		left: Box<JsonExpression>,
93		right: Box<JsonExpression>,
94	},
95	LessThanEqual {
96		left: Box<JsonExpression>,
97		right: Box<JsonExpression>,
98	},
99	Equal {
100		left: Box<JsonExpression>,
101		right: Box<JsonExpression>,
102	},
103	NotEqual {
104		left: Box<JsonExpression>,
105		right: Box<JsonExpression>,
106	},
107
108	// Logical
109	And {
110		left: Box<JsonExpression>,
111		right: Box<JsonExpression>,
112	},
113	Or {
114		left: Box<JsonExpression>,
115		right: Box<JsonExpression>,
116	},
117	Xor {
118		left: Box<JsonExpression>,
119		right: Box<JsonExpression>,
120	},
121
122	// Arithmetic
123	Add {
124		left: Box<JsonExpression>,
125		right: Box<JsonExpression>,
126	},
127	Sub {
128		left: Box<JsonExpression>,
129		right: Box<JsonExpression>,
130	},
131	Mul {
132		left: Box<JsonExpression>,
133		right: Box<JsonExpression>,
134	},
135	Div {
136		left: Box<JsonExpression>,
137		right: Box<JsonExpression>,
138	},
139	Rem {
140		left: Box<JsonExpression>,
141		right: Box<JsonExpression>,
142	},
143
144	// Complex
145	Alias {
146		alias: String,
147		expression: Box<JsonExpression>,
148	},
149	Cast {
150		expression: Box<JsonExpression>,
151		to: String,
152	},
153	Call {
154		function: String,
155		args: Vec<JsonExpression>,
156	},
157	Tuple {
158		expressions: Vec<JsonExpression>,
159	},
160	List {
161		expressions: Vec<JsonExpression>,
162	},
163	Prefix {
164		operator: String,
165		expression: Box<JsonExpression>,
166	},
167	Between {
168		value: Box<JsonExpression>,
169		lower: Box<JsonExpression>,
170		upper: Box<JsonExpression>,
171	},
172	In {
173		value: Box<JsonExpression>,
174		list: Box<JsonExpression>,
175		negated: bool,
176	},
177	Contains {
178		value: Box<JsonExpression>,
179		list: Box<JsonExpression>,
180	},
181	If {
182		condition: Box<JsonExpression>,
183		then: Box<JsonExpression>,
184		else_ifs: Vec<JsonElseIf>,
185		#[serde(skip_serializing_if = "Option::is_none")]
186		else_expr: Option<Box<JsonExpression>>,
187	},
188	Map {
189		expressions: Vec<JsonExpression>,
190	},
191	Extend {
192		expressions: Vec<JsonExpression>,
193	},
194	Type {
195		ty: String,
196	},
197	FieldAccess {
198		object: Box<JsonExpression>,
199		field: String,
200	},
201}
202
203/// JSON representation of an else-if branch.
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
205pub struct JsonElseIf {
206	pub condition: Box<JsonExpression>,
207	pub then: Box<JsonExpression>,
208}
209
210// Helper to extract namespace and primitive from ColumnPrimitive
211fn extract_primitive(cp: &ColumnPrimitive) -> (String, String) {
212	match cp {
213		ColumnPrimitive::Primitive {
214			namespace,
215			primitive,
216		} => (namespace.text().to_string(), primitive.text().to_string()),
217		ColumnPrimitive::Alias(alias) => ("_alias".to_string(), alias.text().to_string()),
218	}
219}
220
221// Helper to create an internal fragment
222fn internal_fragment(text: &str) -> Fragment {
223	Fragment::Internal {
224		text: Arc::from(text),
225	}
226}
227
228impl From<&Expression> for JsonExpression {
229	fn from(expr: &Expression) -> Self {
230		match expr {
231			// Constants
232			Expression::Constant(constant) => match constant {
233				ConstantExpression::None {
234					..
235				} => JsonExpression::None,
236				ConstantExpression::Bool {
237					fragment,
238				} => JsonExpression::Bool {
239					value: fragment.text().to_string(),
240				},
241				ConstantExpression::Number {
242					fragment,
243				} => JsonExpression::Number {
244					value: fragment.text().to_string(),
245				},
246				ConstantExpression::Text {
247					fragment,
248				} => JsonExpression::Text {
249					value: fragment.text().to_string(),
250				},
251				ConstantExpression::Temporal {
252					fragment,
253				} => JsonExpression::Temporal {
254					value: fragment.text().to_string(),
255				},
256			},
257
258			// Identifiers
259			Expression::Column(ColumnExpression(col)) => {
260				let (namespace, source) = extract_primitive(&col.primitive);
261				JsonExpression::Column {
262					namespace,
263					source,
264					name: col.name.text().to_string(),
265				}
266			}
267			Expression::AccessSource(AccessPrimitiveExpression {
268				column,
269			}) => {
270				let (namespace, source) = extract_primitive(&column.primitive);
271				JsonExpression::AccessSource {
272					namespace,
273					source,
274					name: column.name.text().to_string(),
275				}
276			}
277			Expression::Variable(var) => JsonExpression::Variable {
278				name: var.name().to_string(),
279			},
280			Expression::Parameter(param) => match param {
281				ParameterExpression::Positional {
282					fragment,
283				} => JsonExpression::ParameterPositional {
284					position: fragment.text()[1..].to_string(), // Skip '$'
285				},
286				ParameterExpression::Named {
287					fragment,
288				} => JsonExpression::ParameterNamed {
289					name: fragment.text()[1..].to_string(), // Skip '$'
290				},
291			},
292
293			// Comparison
294			Expression::GreaterThan(e) => JsonExpression::GreaterThan {
295				left: Box::new((&*e.left).into()),
296				right: Box::new((&*e.right).into()),
297			},
298			Expression::GreaterThanEqual(e) => JsonExpression::GreaterThanEqual {
299				left: Box::new((&*e.left).into()),
300				right: Box::new((&*e.right).into()),
301			},
302			Expression::LessThan(e) => JsonExpression::LessThan {
303				left: Box::new((&*e.left).into()),
304				right: Box::new((&*e.right).into()),
305			},
306			Expression::LessThanEqual(e) => JsonExpression::LessThanEqual {
307				left: Box::new((&*e.left).into()),
308				right: Box::new((&*e.right).into()),
309			},
310			Expression::Equal(e) => JsonExpression::Equal {
311				left: Box::new((&*e.left).into()),
312				right: Box::new((&*e.right).into()),
313			},
314			Expression::NotEqual(e) => JsonExpression::NotEqual {
315				left: Box::new((&*e.left).into()),
316				right: Box::new((&*e.right).into()),
317			},
318
319			// Logical
320			Expression::And(e) => JsonExpression::And {
321				left: Box::new((&*e.left).into()),
322				right: Box::new((&*e.right).into()),
323			},
324			Expression::Or(e) => JsonExpression::Or {
325				left: Box::new((&*e.left).into()),
326				right: Box::new((&*e.right).into()),
327			},
328			Expression::Xor(e) => JsonExpression::Xor {
329				left: Box::new((&*e.left).into()),
330				right: Box::new((&*e.right).into()),
331			},
332
333			// Arithmetic
334			Expression::Add(e) => JsonExpression::Add {
335				left: Box::new((&*e.left).into()),
336				right: Box::new((&*e.right).into()),
337			},
338			Expression::Sub(e) => JsonExpression::Sub {
339				left: Box::new((&*e.left).into()),
340				right: Box::new((&*e.right).into()),
341			},
342			Expression::Mul(e) => JsonExpression::Mul {
343				left: Box::new((&*e.left).into()),
344				right: Box::new((&*e.right).into()),
345			},
346			Expression::Div(e) => JsonExpression::Div {
347				left: Box::new((&*e.left).into()),
348				right: Box::new((&*e.right).into()),
349			},
350			Expression::Rem(e) => JsonExpression::Rem {
351				left: Box::new((&*e.left).into()),
352				right: Box::new((&*e.right).into()),
353			},
354
355			// Complex
356			Expression::Alias(e) => JsonExpression::Alias {
357				alias: e.alias.name().to_string(),
358				expression: Box::new((&*e.expression).into()),
359			},
360			Expression::Cast(e) => JsonExpression::Cast {
361				expression: Box::new((&*e.expression).into()),
362				to: format!("{:?}", e.to.ty),
363			},
364			Expression::Call(e) => JsonExpression::Call {
365				function: e.func.name().to_string(),
366				args: e.args.iter().map(|a| a.into()).collect(),
367			},
368			Expression::Tuple(e) => JsonExpression::Tuple {
369				expressions: e.expressions.iter().map(|a| a.into()).collect(),
370			},
371			Expression::List(e) => JsonExpression::List {
372				expressions: e.expressions.iter().map(|a| a.into()).collect(),
373			},
374			Expression::Prefix(e) => {
375				let operator = match &e.operator {
376					PrefixOperator::Minus(_) => "-",
377					PrefixOperator::Plus(_) => "+",
378					PrefixOperator::Not(_) => "not",
379				};
380				JsonExpression::Prefix {
381					operator: operator.to_string(),
382					expression: Box::new((&*e.expression).into()),
383				}
384			}
385			Expression::Between(e) => JsonExpression::Between {
386				value: Box::new((&*e.value).into()),
387				lower: Box::new((&*e.lower).into()),
388				upper: Box::new((&*e.upper).into()),
389			},
390			Expression::In(e) => JsonExpression::In {
391				value: Box::new((&*e.value).into()),
392				list: Box::new((&*e.list).into()),
393				negated: e.negated,
394			},
395			Expression::Contains(e) => JsonExpression::Contains {
396				value: Box::new((&*e.value).into()),
397				list: Box::new((&*e.list).into()),
398			},
399			Expression::If(e) => JsonExpression::If {
400				condition: Box::new((&*e.condition).into()),
401				then: Box::new((&*e.then_expr).into()),
402				else_ifs: e
403					.else_ifs
404					.iter()
405					.map(|ei| JsonElseIf {
406						condition: Box::new((&*ei.condition).into()),
407						then: Box::new((&*ei.then_expr).into()),
408					})
409					.collect(),
410				else_expr: e.else_expr.as_ref().map(|ee| Box::new((&**ee).into())),
411			},
412			Expression::Map(e) => JsonExpression::Map {
413				expressions: e.expressions.iter().map(|a| a.into()).collect(),
414			},
415			Expression::Extend(e) => JsonExpression::Extend {
416				expressions: e.expressions.iter().map(|a| a.into()).collect(),
417			},
418			Expression::Type(e) => JsonExpression::Type {
419				ty: format!("{:?}", e.ty),
420			},
421			Expression::SumTypeConstructor(_) => JsonExpression::Type {
422				ty: "SumTypeConstructor".to_string(),
423			},
424			Expression::IsVariant(_) => JsonExpression::Type {
425				ty: "IsVariant".to_string(),
426			},
427			Expression::FieldAccess(e) => JsonExpression::FieldAccess {
428				object: Box::new((&*e.object).into()),
429				field: e.field.text().to_string(),
430			},
431		}
432	}
433}
434
435impl TryFrom<JsonExpression> for Expression {
436	type Error = Error;
437
438	fn try_from(json: JsonExpression) -> Result<Self> {
439		Ok(match json {
440			// Constants
441			JsonExpression::None => Expression::Constant(ConstantExpression::None {
442				fragment: Fragment::None,
443			}),
444			JsonExpression::Bool {
445				value,
446			} => Expression::Constant(ConstantExpression::Bool {
447				fragment: internal_fragment(&value),
448			}),
449			JsonExpression::Number {
450				value,
451			} => Expression::Constant(ConstantExpression::Number {
452				fragment: internal_fragment(&value),
453			}),
454			JsonExpression::Text {
455				value,
456			} => Expression::Constant(ConstantExpression::Text {
457				fragment: internal_fragment(&value),
458			}),
459			JsonExpression::Temporal {
460				value,
461			} => Expression::Constant(ConstantExpression::Temporal {
462				fragment: internal_fragment(&value),
463			}),
464
465			// Identifiers
466			JsonExpression::Column {
467				namespace,
468				source: primitive,
469				name,
470			} => Expression::Column(ColumnExpression(ColumnIdentifier {
471				primitive: ColumnPrimitive::Primitive {
472					namespace: internal_fragment(&namespace),
473					primitive: internal_fragment(&primitive),
474				},
475				name: internal_fragment(&name),
476			})),
477			JsonExpression::AccessSource {
478				namespace,
479				source: primitive,
480				name,
481			} => Expression::AccessSource(AccessPrimitiveExpression {
482				column: ColumnIdentifier {
483					primitive: ColumnPrimitive::Primitive {
484						namespace: internal_fragment(&namespace),
485						primitive: internal_fragment(&primitive),
486					},
487					name: internal_fragment(&name),
488				},
489			}),
490			JsonExpression::Variable {
491				name,
492			} => Expression::Variable(VariableExpression {
493				fragment: internal_fragment(&format!("${}", name)),
494			}),
495			JsonExpression::ParameterPositional {
496				position,
497			} => Expression::Parameter(ParameterExpression::Positional {
498				fragment: internal_fragment(&format!("${}", position)),
499			}),
500			JsonExpression::ParameterNamed {
501				name,
502			} => Expression::Parameter(ParameterExpression::Named {
503				fragment: internal_fragment(&format!("${}", name)),
504			}),
505
506			// Comparison
507			JsonExpression::GreaterThan {
508				left,
509				right,
510			} => Expression::GreaterThan(GreaterThanExpression {
511				left: Box::new((*left).try_into()?),
512				right: Box::new((*right).try_into()?),
513				fragment: Fragment::None,
514			}),
515			JsonExpression::GreaterThanEqual {
516				left,
517				right,
518			} => Expression::GreaterThanEqual(GreaterThanEqExpression {
519				left: Box::new((*left).try_into()?),
520				right: Box::new((*right).try_into()?),
521				fragment: Fragment::None,
522			}),
523			JsonExpression::LessThan {
524				left,
525				right,
526			} => Expression::LessThan(LessThanExpression {
527				left: Box::new((*left).try_into()?),
528				right: Box::new((*right).try_into()?),
529				fragment: Fragment::None,
530			}),
531			JsonExpression::LessThanEqual {
532				left,
533				right,
534			} => Expression::LessThanEqual(LessThanEqExpression {
535				left: Box::new((*left).try_into()?),
536				right: Box::new((*right).try_into()?),
537				fragment: Fragment::None,
538			}),
539			JsonExpression::Equal {
540				left,
541				right,
542			} => Expression::Equal(EqExpression {
543				left: Box::new((*left).try_into()?),
544				right: Box::new((*right).try_into()?),
545				fragment: Fragment::None,
546			}),
547			JsonExpression::NotEqual {
548				left,
549				right,
550			} => Expression::NotEqual(NotEqExpression {
551				left: Box::new((*left).try_into()?),
552				right: Box::new((*right).try_into()?),
553				fragment: Fragment::None,
554			}),
555
556			// Logical
557			JsonExpression::And {
558				left,
559				right,
560			} => Expression::And(AndExpression {
561				left: Box::new((*left).try_into()?),
562				right: Box::new((*right).try_into()?),
563				fragment: Fragment::None,
564			}),
565			JsonExpression::Or {
566				left,
567				right,
568			} => Expression::Or(OrExpression {
569				left: Box::new((*left).try_into()?),
570				right: Box::new((*right).try_into()?),
571				fragment: Fragment::None,
572			}),
573			JsonExpression::Xor {
574				left,
575				right,
576			} => Expression::Xor(XorExpression {
577				left: Box::new((*left).try_into()?),
578				right: Box::new((*right).try_into()?),
579				fragment: Fragment::None,
580			}),
581
582			// Arithmetic
583			JsonExpression::Add {
584				left,
585				right,
586			} => Expression::Add(AddExpression {
587				left: Box::new((*left).try_into()?),
588				right: Box::new((*right).try_into()?),
589				fragment: Fragment::None,
590			}),
591			JsonExpression::Sub {
592				left,
593				right,
594			} => Expression::Sub(SubExpression {
595				left: Box::new((*left).try_into()?),
596				right: Box::new((*right).try_into()?),
597				fragment: Fragment::None,
598			}),
599			JsonExpression::Mul {
600				left,
601				right,
602			} => Expression::Mul(MulExpression {
603				left: Box::new((*left).try_into()?),
604				right: Box::new((*right).try_into()?),
605				fragment: Fragment::None,
606			}),
607			JsonExpression::Div {
608				left,
609				right,
610			} => Expression::Div(DivExpression {
611				left: Box::new((*left).try_into()?),
612				right: Box::new((*right).try_into()?),
613				fragment: Fragment::None,
614			}),
615			JsonExpression::Rem {
616				left,
617				right,
618			} => Expression::Rem(RemExpression {
619				left: Box::new((*left).try_into()?),
620				right: Box::new((*right).try_into()?),
621				fragment: Fragment::None,
622			}),
623
624			// Complex
625			JsonExpression::Alias {
626				alias,
627				expression,
628			} => Expression::Alias(AliasExpression {
629				alias: IdentExpression(internal_fragment(&alias)),
630				expression: Box::new((*expression).try_into()?),
631				fragment: Fragment::None,
632			}),
633			JsonExpression::Cast {
634				expression,
635				to,
636			} => {
637				let ty = parse_type(&to)?;
638				Expression::Cast(CastExpression {
639					expression: Box::new((*expression).try_into()?),
640					to: TypeExpression {
641						ty,
642						fragment: internal_fragment(&to),
643					},
644					fragment: Fragment::None,
645				})
646			}
647			JsonExpression::Call {
648				function,
649				args,
650			} => Expression::Call(CallExpression {
651				func: IdentExpression(internal_fragment(&function)),
652				args: args.into_iter().map(|a| a.try_into()).collect::<Result<Vec<_>>>()?,
653				fragment: Fragment::None,
654			}),
655			JsonExpression::Tuple {
656				expressions,
657			} => Expression::Tuple(TupleExpression {
658				expressions: expressions
659					.into_iter()
660					.map(|a| a.try_into())
661					.collect::<Result<Vec<_>>>()?,
662				fragment: Fragment::None,
663			}),
664			JsonExpression::List {
665				expressions,
666			} => Expression::List(ListExpression {
667				expressions: expressions
668					.into_iter()
669					.map(|a| a.try_into())
670					.collect::<Result<Vec<_>>>()?,
671				fragment: Fragment::None,
672			}),
673			JsonExpression::Prefix {
674				operator,
675				expression,
676			} => {
677				let op = match operator.as_str() {
678					"-" => PrefixOperator::Minus(Fragment::None),
679					"+" => PrefixOperator::Plus(Fragment::None),
680					"not" => PrefixOperator::Not(Fragment::None),
681					_ => {
682						return Err(Error(internal!("Unknown prefix operator: {}", operator)));
683					}
684				};
685				Expression::Prefix(PrefixExpression {
686					operator: op,
687					expression: Box::new((*expression).try_into()?),
688					fragment: Fragment::None,
689				})
690			}
691			JsonExpression::Between {
692				value,
693				lower,
694				upper,
695			} => Expression::Between(BetweenExpression {
696				value: Box::new((*value).try_into()?),
697				lower: Box::new((*lower).try_into()?),
698				upper: Box::new((*upper).try_into()?),
699				fragment: Fragment::None,
700			}),
701			JsonExpression::In {
702				value,
703				list,
704				negated,
705			} => Expression::In(InExpression {
706				value: Box::new((*value).try_into()?),
707				list: Box::new((*list).try_into()?),
708				negated,
709				fragment: Fragment::None,
710			}),
711			JsonExpression::Contains {
712				value,
713				list,
714			} => Expression::Contains(ContainsExpression {
715				value: Box::new((*value).try_into()?),
716				list: Box::new((*list).try_into()?),
717				fragment: Fragment::None,
718			}),
719			JsonExpression::If {
720				condition,
721				then,
722				else_ifs,
723				else_expr,
724			} => {
725				let converted_else: Option<Box<Expression>> = match else_expr {
726					Some(ee) => Some(Box::new((*ee).try_into()?)),
727					None => None,
728				};
729				Expression::If(IfExpression {
730					condition: Box::new((*condition).try_into()?),
731					then_expr: Box::new((*then).try_into()?),
732					else_ifs: else_ifs
733						.into_iter()
734						.map(|ei| {
735							Ok(ElseIfExpression {
736								condition: Box::new((*ei.condition).try_into()?),
737								then_expr: Box::new((*ei.then).try_into()?),
738								fragment: Fragment::None,
739							})
740						})
741						.collect::<Result<Vec<_>>>()?,
742					else_expr: converted_else,
743					fragment: Fragment::None,
744				})
745			}
746			JsonExpression::Map {
747				expressions,
748			} => Expression::Map(MapExpression {
749				expressions: expressions
750					.into_iter()
751					.map(|a| a.try_into())
752					.collect::<Result<Vec<_>>>()?,
753				fragment: Fragment::None,
754			}),
755			JsonExpression::Extend {
756				expressions,
757			} => Expression::Extend(ExtendExpression {
758				expressions: expressions
759					.into_iter()
760					.map(|a| a.try_into())
761					.collect::<Result<Vec<_>>>()?,
762				fragment: Fragment::None,
763			}),
764			JsonExpression::Type {
765				ty,
766			} => {
767				let parsed_ty = parse_type(&ty)?;
768				Expression::Type(TypeExpression {
769					ty: parsed_ty,
770					fragment: internal_fragment(&ty),
771				})
772			}
773			JsonExpression::FieldAccess {
774				object,
775				field,
776			} => Expression::FieldAccess(FieldAccessExpression {
777				object: Box::new((*object).try_into()?),
778				field: internal_fragment(&field),
779				fragment: Fragment::None,
780			}),
781		})
782	}
783}
784
785// Helper to parse type strings back to Type enum
786fn parse_type(s: &str) -> Result<Type> {
787	// Handle type debug representations
788	let ty = match s.to_lowercase().as_str() {
789		"boolean" => Type::Boolean,
790		"bool" => Type::Boolean,
791		"int1" => Type::Int1,
792		"int2" => Type::Int2,
793		"int4" => Type::Int4,
794		"int8" => Type::Int8,
795		"int16" => Type::Int16,
796		"int32" => Type::Int4,
797		"int64" => Type::Int8,
798		"uint1" => Type::Uint1,
799		"uint2" => Type::Uint2,
800		"uint4" => Type::Uint4,
801		"uint8" => Type::Uint8,
802		"uint16" => Type::Uint16,
803		"float4" => Type::Float4,
804		"float8" => Type::Float8,
805		"float32" => Type::Float4,
806		"float64" => Type::Float8,
807		"utf8" => Type::Utf8,
808		"string" => Type::Utf8,
809		"text" => Type::Utf8,
810		"blob" => Type::Blob,
811		"uuid4" => Type::Uuid4,
812		"uuid7" => Type::Uuid7,
813		"date" => Type::Date,
814		"time" => Type::Time,
815		"datetime" => Type::DateTime,
816		"duration" => Type::Duration,
817		"identityid" => Type::IdentityId,
818		"int" => Type::Int,
819		"uint" => Type::Uint,
820		"decimal" => Type::Decimal,
821		_ => {
822			return Err(Error(internal!("Unknown type: {}", s)));
823		}
824	};
825
826	Ok(ty)
827}
828
829/// Serialize an Expression to a JSON string.
830///
831/// The output skips Fragment source location metadata and preserves only
832/// semantic data suitable for frontend query builders.
833pub fn to_json(expr: &Expression) -> String {
834	let json_expr: JsonExpression = expr.into();
835	to_string(&json_expr).expect("JsonExpression should always serialize")
836}
837
838/// Serialize an Expression to a pretty-printed JSON string.
839pub fn to_json_pretty(expr: &Expression) -> String {
840	let json_expr: JsonExpression = expr.into();
841	to_string_pretty(&json_expr).expect("JsonExpression should always serialize")
842}
843
844/// Deserialize an Expression from a JSON string.
845pub fn from_json(json: &str) -> Result<Expression> {
846	let json_expr: JsonExpression = from_str(json).map_err(|e| {
847		Error::from(TypeError::SerdeDeserialize {
848			message: e.to_string(),
849		})
850	})?;
851	json_expr.try_into()
852}
853
854#[cfg(test)]
855pub mod tests {
856	use super::*;
857
858	// Helper functions to create test expressions
859	fn column_expr(name: &str) -> Expression {
860		Expression::Column(ColumnExpression(ColumnIdentifier {
861			primitive: ColumnPrimitive::Primitive {
862				namespace: internal_fragment("_context"),
863				primitive: internal_fragment("_context"),
864			},
865			name: internal_fragment(name),
866		}))
867	}
868
869	fn constant_number(val: &str) -> Expression {
870		Expression::Constant(ConstantExpression::Number {
871			fragment: internal_fragment(val),
872		})
873	}
874
875	fn constant_text(val: &str) -> Expression {
876		Expression::Constant(ConstantExpression::Text {
877			fragment: internal_fragment(val),
878		})
879	}
880
881	fn constant_bool(val: &str) -> Expression {
882		Expression::Constant(ConstantExpression::Bool {
883			fragment: internal_fragment(val),
884		})
885	}
886
887	// =========================================================================
888	// Constant tests
889	// =========================================================================
890
891	#[test]
892	fn test_undefined() {
893		let expr = Expression::Constant(ConstantExpression::None {
894			fragment: Fragment::None,
895		});
896
897		let json = to_json(&expr);
898		assert_eq!(json, r#"{"type":"none"}"#);
899
900		let recovered = from_json(&json).unwrap();
901		assert_eq!(to_json(&recovered), json);
902	}
903
904	#[test]
905	fn test_bool() {
906		let expr = constant_bool("true");
907
908		let json = to_json(&expr);
909		assert_eq!(json, r#"{"type":"bool","value":"true"}"#);
910
911		let recovered = from_json(&json).unwrap();
912		assert_eq!(to_json(&recovered), json);
913	}
914
915	#[test]
916	fn test_number() {
917		let expr = constant_number("42");
918
919		let json = to_json(&expr);
920		assert_eq!(json, r#"{"type":"number","value":"42"}"#);
921
922		let recovered = from_json(&json).unwrap();
923		assert_eq!(to_json(&recovered), json);
924	}
925
926	#[test]
927	fn test_text() {
928		let expr = constant_text("hello world");
929
930		let json = to_json(&expr);
931		assert_eq!(json, r#"{"type":"text","value":"hello world"}"#);
932
933		let recovered = from_json(&json).unwrap();
934		assert_eq!(to_json(&recovered), json);
935	}
936
937	#[test]
938	fn test_temporal() {
939		let expr = Expression::Constant(ConstantExpression::Temporal {
940			fragment: internal_fragment("2024-01-15T10:30:00"),
941		});
942
943		let json = to_json(&expr);
944		assert_eq!(json, r#"{"type":"temporal","value":"2024-01-15T10:30:00"}"#);
945
946		let recovered = from_json(&json).unwrap();
947		assert_eq!(to_json(&recovered), json);
948	}
949
950	// =========================================================================
951	// Identifier tests
952	// =========================================================================
953
954	#[test]
955	fn test_column() {
956		let expr = column_expr("age");
957
958		let json = to_json(&expr);
959		assert_eq!(json, r#"{"type":"column","namespace":"_context","source":"_context","name":"age"}"#);
960
961		let recovered = from_json(&json).unwrap();
962		assert_eq!(to_json(&recovered), json);
963	}
964
965	#[test]
966	fn test_variable() {
967		let expr = Expression::Variable(VariableExpression {
968			fragment: internal_fragment("$my_var"),
969		});
970
971		let json = to_json(&expr);
972		assert_eq!(json, r#"{"type":"variable","name":"my_var"}"#);
973
974		let recovered = from_json(&json).unwrap();
975		assert_eq!(to_json(&recovered), json);
976	}
977
978	#[test]
979	fn test_parameter_positional() {
980		let expr = Expression::Parameter(ParameterExpression::Positional {
981			fragment: internal_fragment("$1"),
982		});
983
984		let json = to_json(&expr);
985		assert_eq!(json, r#"{"type":"parameter_positional","position":"1"}"#);
986
987		let recovered = from_json(&json).unwrap();
988		assert_eq!(to_json(&recovered), json);
989	}
990
991	#[test]
992	fn test_parameter_named() {
993		let expr = Expression::Parameter(ParameterExpression::Named {
994			fragment: internal_fragment("$name"),
995		});
996
997		let json = to_json(&expr);
998		assert_eq!(json, r#"{"type":"parameter_named","name":"name"}"#);
999
1000		let recovered = from_json(&json).unwrap();
1001		assert_eq!(to_json(&recovered), json);
1002	}
1003
1004	// =========================================================================
1005	// Comparison tests
1006	// =========================================================================
1007
1008	#[test]
1009	fn test_greater_than() {
1010		let expr = Expression::GreaterThan(GreaterThanExpression {
1011			left: Box::new(column_expr("age")),
1012			right: Box::new(constant_number("18")),
1013			fragment: Fragment::None,
1014		});
1015
1016		let json = to_json(&expr);
1017		assert_eq!(
1018			json,
1019			r#"{"type":"greater_than","left":{"type":"column","namespace":"_context","source":"_context","name":"age"},"right":{"type":"number","value":"18"}}"#
1020		);
1021
1022		let recovered = from_json(&json).unwrap();
1023		assert_eq!(to_json(&recovered), json);
1024	}
1025
1026	#[test]
1027	fn test_greater_than_equal() {
1028		let expr = Expression::GreaterThanEqual(GreaterThanEqExpression {
1029			left: Box::new(column_expr("price")),
1030			right: Box::new(constant_number("100")),
1031			fragment: Fragment::None,
1032		});
1033
1034		let json = to_json(&expr);
1035		assert_eq!(
1036			json,
1037			r#"{"type":"greater_than_equal","left":{"type":"column","namespace":"_context","source":"_context","name":"price"},"right":{"type":"number","value":"100"}}"#
1038		);
1039
1040		let recovered = from_json(&json).unwrap();
1041		assert_eq!(to_json(&recovered), json);
1042	}
1043
1044	#[test]
1045	fn test_less_than() {
1046		let expr = Expression::LessThan(LessThanExpression {
1047			left: Box::new(column_expr("count")),
1048			right: Box::new(constant_number("10")),
1049			fragment: Fragment::None,
1050		});
1051
1052		let json = to_json(&expr);
1053		assert_eq!(
1054			json,
1055			r#"{"type":"less_than","left":{"type":"column","namespace":"_context","source":"_context","name":"count"},"right":{"type":"number","value":"10"}}"#
1056		);
1057
1058		let recovered = from_json(&json).unwrap();
1059		assert_eq!(to_json(&recovered), json);
1060	}
1061
1062	#[test]
1063	fn test_less_than_equal() {
1064		let expr = Expression::LessThanEqual(LessThanEqExpression {
1065			left: Box::new(column_expr("quantity")),
1066			right: Box::new(constant_number("5")),
1067			fragment: Fragment::None,
1068		});
1069
1070		let json = to_json(&expr);
1071		assert_eq!(
1072			json,
1073			r#"{"type":"less_than_equal","left":{"type":"column","namespace":"_context","source":"_context","name":"quantity"},"right":{"type":"number","value":"5"}}"#
1074		);
1075
1076		let recovered = from_json(&json).unwrap();
1077		assert_eq!(to_json(&recovered), json);
1078	}
1079
1080	#[test]
1081	fn test_equal() {
1082		let expr = Expression::Equal(EqExpression {
1083			left: Box::new(column_expr("status")),
1084			right: Box::new(constant_text("active")),
1085			fragment: Fragment::None,
1086		});
1087
1088		let json = to_json(&expr);
1089		assert_eq!(
1090			json,
1091			r#"{"type":"equal","left":{"type":"column","namespace":"_context","source":"_context","name":"status"},"right":{"type":"text","value":"active"}}"#
1092		);
1093
1094		let recovered = from_json(&json).unwrap();
1095		assert_eq!(to_json(&recovered), json);
1096	}
1097
1098	#[test]
1099	fn test_not_equal() {
1100		let expr = Expression::NotEqual(NotEqExpression {
1101			left: Box::new(column_expr("type")),
1102			right: Box::new(constant_text("deleted")),
1103			fragment: Fragment::None,
1104		});
1105
1106		let json = to_json(&expr);
1107		assert_eq!(
1108			json,
1109			r#"{"type":"not_equal","left":{"type":"column","namespace":"_context","source":"_context","name":"type"},"right":{"type":"text","value":"deleted"}}"#
1110		);
1111
1112		let recovered = from_json(&json).unwrap();
1113		assert_eq!(to_json(&recovered), json);
1114	}
1115
1116	// =========================================================================
1117	// Logical tests
1118	// =========================================================================
1119
1120	#[test]
1121	fn test_and() {
1122		let expr = Expression::And(AndExpression {
1123			left: Box::new(Expression::GreaterThan(GreaterThanExpression {
1124				left: Box::new(column_expr("age")),
1125				right: Box::new(constant_number("18")),
1126				fragment: Fragment::None,
1127			})),
1128			right: Box::new(Expression::Equal(EqExpression {
1129				left: Box::new(column_expr("active")),
1130				right: Box::new(constant_bool("true")),
1131				fragment: Fragment::None,
1132			})),
1133			fragment: Fragment::None,
1134		});
1135
1136		let json = to_json(&expr);
1137		assert_eq!(
1138			json,
1139			r#"{"type":"and","left":{"type":"greater_than","left":{"type":"column","namespace":"_context","source":"_context","name":"age"},"right":{"type":"number","value":"18"}},"right":{"type":"equal","left":{"type":"column","namespace":"_context","source":"_context","name":"active"},"right":{"type":"bool","value":"true"}}}"#
1140		);
1141
1142		let recovered = from_json(&json).unwrap();
1143		assert_eq!(to_json(&recovered), json);
1144	}
1145
1146	#[test]
1147	fn test_or() {
1148		let expr = Expression::Or(OrExpression {
1149			left: Box::new(column_expr("a")),
1150			right: Box::new(column_expr("b")),
1151			fragment: Fragment::None,
1152		});
1153
1154		let json = to_json(&expr);
1155		assert_eq!(
1156			json,
1157			r#"{"type":"or","left":{"type":"column","namespace":"_context","source":"_context","name":"a"},"right":{"type":"column","namespace":"_context","source":"_context","name":"b"}}"#
1158		);
1159
1160		let recovered = from_json(&json).unwrap();
1161		assert_eq!(to_json(&recovered), json);
1162	}
1163
1164	#[test]
1165	fn test_xor() {
1166		let expr = Expression::Xor(XorExpression {
1167			left: Box::new(column_expr("x")),
1168			right: Box::new(column_expr("y")),
1169			fragment: Fragment::None,
1170		});
1171
1172		let json = to_json(&expr);
1173		assert_eq!(
1174			json,
1175			r#"{"type":"xor","left":{"type":"column","namespace":"_context","source":"_context","name":"x"},"right":{"type":"column","namespace":"_context","source":"_context","name":"y"}}"#
1176		);
1177
1178		let recovered = from_json(&json).unwrap();
1179		assert_eq!(to_json(&recovered), json);
1180	}
1181
1182	// =========================================================================
1183	// Arithmetic tests
1184	// =========================================================================
1185
1186	#[test]
1187	fn test_add() {
1188		let expr = Expression::Add(AddExpression {
1189			left: Box::new(column_expr("price")),
1190			right: Box::new(constant_number("10")),
1191			fragment: Fragment::None,
1192		});
1193
1194		let json = to_json(&expr);
1195		assert_eq!(
1196			json,
1197			r#"{"type":"add","left":{"type":"column","namespace":"_context","source":"_context","name":"price"},"right":{"type":"number","value":"10"}}"#
1198		);
1199
1200		let recovered = from_json(&json).unwrap();
1201		assert_eq!(to_json(&recovered), json);
1202	}
1203
1204	#[test]
1205	fn test_sub() {
1206		let expr = Expression::Sub(SubExpression {
1207			left: Box::new(column_expr("total")),
1208			right: Box::new(constant_number("5")),
1209			fragment: Fragment::None,
1210		});
1211
1212		let json = to_json(&expr);
1213		assert_eq!(
1214			json,
1215			r#"{"type":"sub","left":{"type":"column","namespace":"_context","source":"_context","name":"total"},"right":{"type":"number","value":"5"}}"#
1216		);
1217
1218		let recovered = from_json(&json).unwrap();
1219		assert_eq!(to_json(&recovered), json);
1220	}
1221
1222	#[test]
1223	fn test_mul() {
1224		let expr = Expression::Mul(MulExpression {
1225			left: Box::new(column_expr("qty")),
1226			right: Box::new(constant_number("2")),
1227			fragment: Fragment::None,
1228		});
1229
1230		let json = to_json(&expr);
1231		assert_eq!(
1232			json,
1233			r#"{"type":"mul","left":{"type":"column","namespace":"_context","source":"_context","name":"qty"},"right":{"type":"number","value":"2"}}"#
1234		);
1235
1236		let recovered = from_json(&json).unwrap();
1237		assert_eq!(to_json(&recovered), json);
1238	}
1239
1240	#[test]
1241	fn test_div() {
1242		let expr = Expression::Div(DivExpression {
1243			left: Box::new(column_expr("amount")),
1244			right: Box::new(constant_number("4")),
1245			fragment: Fragment::None,
1246		});
1247
1248		let json = to_json(&expr);
1249		assert_eq!(
1250			json,
1251			r#"{"type":"div","left":{"type":"column","namespace":"_context","source":"_context","name":"amount"},"right":{"type":"number","value":"4"}}"#
1252		);
1253
1254		let recovered = from_json(&json).unwrap();
1255		assert_eq!(to_json(&recovered), json);
1256	}
1257
1258	#[test]
1259	fn test_rem() {
1260		let expr = Expression::Rem(RemExpression {
1261			left: Box::new(column_expr("num")),
1262			right: Box::new(constant_number("3")),
1263			fragment: Fragment::None,
1264		});
1265
1266		let json = to_json(&expr);
1267		assert_eq!(
1268			json,
1269			r#"{"type":"rem","left":{"type":"column","namespace":"_context","source":"_context","name":"num"},"right":{"type":"number","value":"3"}}"#
1270		);
1271
1272		let recovered = from_json(&json).unwrap();
1273		assert_eq!(to_json(&recovered), json);
1274	}
1275
1276	// =========================================================================
1277	// Complex expression tests
1278	// =========================================================================
1279
1280	#[test]
1281	fn test_alias() {
1282		let expr = Expression::Alias(AliasExpression {
1283			alias: IdentExpression(internal_fragment("user_name")),
1284			expression: Box::new(column_expr("name")),
1285			fragment: Fragment::None,
1286		});
1287
1288		let json = to_json(&expr);
1289		assert_eq!(
1290			json,
1291			r#"{"type":"alias","alias":"user_name","expression":{"type":"column","namespace":"_context","source":"_context","name":"name"}}"#
1292		);
1293
1294		let recovered = from_json(&json).unwrap();
1295		assert_eq!(to_json(&recovered), json);
1296	}
1297
1298	#[test]
1299	fn test_cast() {
1300		let expr = Expression::Cast(CastExpression {
1301			expression: Box::new(column_expr("value")),
1302			to: TypeExpression {
1303				ty: Type::Int4,
1304				fragment: internal_fragment("Int4"),
1305			},
1306			fragment: Fragment::None,
1307		});
1308
1309		let json = to_json(&expr);
1310		assert_eq!(
1311			json,
1312			r#"{"type":"cast","expression":{"type":"column","namespace":"_context","source":"_context","name":"value"},"to":"Int4"}"#
1313		);
1314
1315		let recovered = from_json(&json).unwrap();
1316		assert_eq!(to_json(&recovered), json);
1317	}
1318
1319	#[test]
1320	fn test_call() {
1321		let expr = Expression::Call(CallExpression {
1322			func: IdentExpression(internal_fragment("math::avg")),
1323			args: vec![column_expr("price")],
1324			fragment: Fragment::None,
1325		});
1326
1327		let json = to_json(&expr);
1328		assert_eq!(
1329			json,
1330			r#"{"type":"call","function":"math::avg","args":[{"type":"column","namespace":"_context","source":"_context","name":"price"}]}"#
1331		);
1332
1333		let recovered = from_json(&json).unwrap();
1334		assert_eq!(to_json(&recovered), json);
1335	}
1336
1337	#[test]
1338	fn test_tuple() {
1339		let expr = Expression::Tuple(TupleExpression {
1340			expressions: vec![constant_number("1"), constant_number("2"), constant_number("3")],
1341			fragment: Fragment::None,
1342		});
1343
1344		let json = to_json(&expr);
1345		assert_eq!(
1346			json,
1347			r#"{"type":"tuple","expressions":[{"type":"number","value":"1"},{"type":"number","value":"2"},{"type":"number","value":"3"}]}"#
1348		);
1349
1350		let recovered = from_json(&json).unwrap();
1351		assert_eq!(to_json(&recovered), json);
1352	}
1353
1354	#[test]
1355	fn test_prefix_minus() {
1356		let expr = Expression::Prefix(PrefixExpression {
1357			operator: PrefixOperator::Minus(Fragment::None),
1358			expression: Box::new(column_expr("value")),
1359			fragment: Fragment::None,
1360		});
1361
1362		let json = to_json(&expr);
1363		assert_eq!(
1364			json,
1365			r#"{"type":"prefix","operator":"-","expression":{"type":"column","namespace":"_context","source":"_context","name":"value"}}"#
1366		);
1367
1368		let recovered = from_json(&json).unwrap();
1369		assert_eq!(to_json(&recovered), json);
1370	}
1371
1372	#[test]
1373	fn test_prefix_not() {
1374		let expr = Expression::Prefix(PrefixExpression {
1375			operator: PrefixOperator::Not(Fragment::None),
1376			expression: Box::new(column_expr("flag")),
1377			fragment: Fragment::None,
1378		});
1379
1380		let json = to_json(&expr);
1381		assert_eq!(
1382			json,
1383			r#"{"type":"prefix","operator":"not","expression":{"type":"column","namespace":"_context","source":"_context","name":"flag"}}"#
1384		);
1385
1386		let recovered = from_json(&json).unwrap();
1387		assert_eq!(to_json(&recovered), json);
1388	}
1389
1390	#[test]
1391	fn test_between() {
1392		let expr = Expression::Between(BetweenExpression {
1393			value: Box::new(column_expr("age")),
1394			lower: Box::new(constant_number("18")),
1395			upper: Box::new(constant_number("65")),
1396			fragment: Fragment::None,
1397		});
1398
1399		let json = to_json(&expr);
1400		assert_eq!(
1401			json,
1402			r#"{"type":"between","value":{"type":"column","namespace":"_context","source":"_context","name":"age"},"lower":{"type":"number","value":"18"},"upper":{"type":"number","value":"65"}}"#
1403		);
1404
1405		let recovered = from_json(&json).unwrap();
1406		assert_eq!(to_json(&recovered), json);
1407	}
1408
1409	#[test]
1410	fn test_in() {
1411		let expr = Expression::In(InExpression {
1412			value: Box::new(column_expr("status")),
1413			list: Box::new(Expression::Tuple(TupleExpression {
1414				expressions: vec![constant_text("active"), constant_text("pending")],
1415				fragment: Fragment::None,
1416			})),
1417			negated: false,
1418			fragment: Fragment::None,
1419		});
1420
1421		let json = to_json(&expr);
1422		assert_eq!(
1423			json,
1424			r#"{"type":"in","value":{"type":"column","namespace":"_context","source":"_context","name":"status"},"list":{"type":"tuple","expressions":[{"type":"text","value":"active"},{"type":"text","value":"pending"}]},"negated":false}"#
1425		);
1426
1427		let recovered = from_json(&json).unwrap();
1428		assert_eq!(to_json(&recovered), json);
1429	}
1430
1431	#[test]
1432	fn test_not_in() {
1433		let expr = Expression::In(InExpression {
1434			value: Box::new(column_expr("type")),
1435			list: Box::new(Expression::Tuple(TupleExpression {
1436				expressions: vec![constant_text("deleted"), constant_text("archived")],
1437				fragment: Fragment::None,
1438			})),
1439			negated: true,
1440			fragment: Fragment::None,
1441		});
1442
1443		let json = to_json(&expr);
1444		assert_eq!(
1445			json,
1446			r#"{"type":"in","value":{"type":"column","namespace":"_context","source":"_context","name":"type"},"list":{"type":"tuple","expressions":[{"type":"text","value":"deleted"},{"type":"text","value":"archived"}]},"negated":true}"#
1447		);
1448
1449		let recovered = from_json(&json).unwrap();
1450		assert_eq!(to_json(&recovered), json);
1451	}
1452
1453	#[test]
1454	fn test_if_simple() {
1455		let expr = Expression::If(IfExpression {
1456			condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1457				left: Box::new(column_expr("age")),
1458				right: Box::new(constant_number("18")),
1459				fragment: Fragment::None,
1460			})),
1461			then_expr: Box::new(constant_text("adult")),
1462			else_ifs: vec![],
1463			else_expr: Some(Box::new(constant_text("minor"))),
1464			fragment: Fragment::None,
1465		});
1466
1467		let json = to_json(&expr);
1468		assert_eq!(
1469			json,
1470			r#"{"type":"if","condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","source":"_context","name":"age"},"right":{"type":"number","value":"18"}},"then":{"type":"text","value":"adult"},"else_ifs":[],"else_expr":{"type":"text","value":"minor"}}"#
1471		);
1472
1473		let recovered = from_json(&json).unwrap();
1474		assert_eq!(to_json(&recovered), json);
1475	}
1476
1477	#[test]
1478	fn test_if_with_else_if() {
1479		let expr = Expression::If(IfExpression {
1480			condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1481				left: Box::new(column_expr("score")),
1482				right: Box::new(constant_number("90")),
1483				fragment: Fragment::None,
1484			})),
1485			then_expr: Box::new(constant_text("A")),
1486			else_ifs: vec![ElseIfExpression {
1487				condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1488					left: Box::new(column_expr("score")),
1489					right: Box::new(constant_number("80")),
1490					fragment: Fragment::None,
1491				})),
1492				then_expr: Box::new(constant_text("B")),
1493				fragment: Fragment::None,
1494			}],
1495			else_expr: Some(Box::new(constant_text("C"))),
1496			fragment: Fragment::None,
1497		});
1498
1499		let json = to_json(&expr);
1500		assert_eq!(
1501			json,
1502			r#"{"type":"if","condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","source":"_context","name":"score"},"right":{"type":"number","value":"90"}},"then":{"type":"text","value":"A"},"else_ifs":[{"condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","source":"_context","name":"score"},"right":{"type":"number","value":"80"}},"then":{"type":"text","value":"B"}}],"else_expr":{"type":"text","value":"C"}}"#
1503		);
1504
1505		let recovered = from_json(&json).unwrap();
1506		assert_eq!(to_json(&recovered), json);
1507	}
1508
1509	#[test]
1510	fn test_map() {
1511		let expr = Expression::Map(MapExpression {
1512			expressions: vec![
1513				Expression::Alias(AliasExpression {
1514					alias: IdentExpression(internal_fragment("user_name")),
1515					expression: Box::new(column_expr("name")),
1516					fragment: Fragment::None,
1517				}),
1518				column_expr("id"),
1519			],
1520			fragment: Fragment::None,
1521		});
1522
1523		let json = to_json(&expr);
1524		assert_eq!(
1525			json,
1526			r#"{"type":"map","expressions":[{"type":"alias","alias":"user_name","expression":{"type":"column","namespace":"_context","source":"_context","name":"name"}},{"type":"column","namespace":"_context","source":"_context","name":"id"}]}"#
1527		);
1528
1529		let recovered = from_json(&json).unwrap();
1530		assert_eq!(to_json(&recovered), json);
1531	}
1532
1533	#[test]
1534	fn test_extend() {
1535		let expr = Expression::Extend(ExtendExpression {
1536			expressions: vec![Expression::Alias(AliasExpression {
1537				alias: IdentExpression(internal_fragment("full_name")),
1538				expression: Box::new(Expression::Add(AddExpression {
1539					left: Box::new(column_expr("first")),
1540					right: Box::new(column_expr("last")),
1541					fragment: Fragment::None,
1542				})),
1543				fragment: Fragment::None,
1544			})],
1545			fragment: Fragment::None,
1546		});
1547
1548		let json = to_json(&expr);
1549		assert_eq!(
1550			json,
1551			r#"{"type":"extend","expressions":[{"type":"alias","alias":"full_name","expression":{"type":"add","left":{"type":"column","namespace":"_context","source":"_context","name":"first"},"right":{"type":"column","namespace":"_context","source":"_context","name":"last"}}}]}"#
1552		);
1553
1554		let recovered = from_json(&json).unwrap();
1555		assert_eq!(to_json(&recovered), json);
1556	}
1557
1558	#[test]
1559	fn test_type_expression() {
1560		let expr = Expression::Type(TypeExpression {
1561			ty: Type::Utf8,
1562			fragment: internal_fragment("Utf8"),
1563		});
1564
1565		let json = to_json(&expr);
1566		assert_eq!(json, r#"{"type":"type","ty":"Utf8"}"#);
1567
1568		let recovered = from_json(&json).unwrap();
1569		assert_eq!(to_json(&recovered), json);
1570	}
1571
1572	// =========================================================================
1573	// Complex nested expression test
1574	// =========================================================================
1575
1576	#[test]
1577	fn test_complex_nested_expression() {
1578		// Build: (age > 18 AND status = 'active') OR (role = 'admin')
1579		let expr = Expression::Or(OrExpression {
1580			left: Box::new(Expression::And(AndExpression {
1581				left: Box::new(Expression::GreaterThan(GreaterThanExpression {
1582					left: Box::new(column_expr("age")),
1583					right: Box::new(constant_number("18")),
1584					fragment: Fragment::None,
1585				})),
1586				right: Box::new(Expression::Equal(EqExpression {
1587					left: Box::new(column_expr("status")),
1588					right: Box::new(constant_text("active")),
1589					fragment: Fragment::None,
1590				})),
1591				fragment: Fragment::None,
1592			})),
1593			right: Box::new(Expression::Equal(EqExpression {
1594				left: Box::new(column_expr("role")),
1595				right: Box::new(constant_text("admin")),
1596				fragment: Fragment::None,
1597			})),
1598			fragment: Fragment::None,
1599		});
1600
1601		let json = to_json(&expr);
1602		let recovered = from_json(&json).unwrap();
1603		assert_eq!(to_json(&recovered), json);
1604
1605		// Verify pretty print works
1606		let pretty = to_json_pretty(&expr);
1607		assert!(pretty.contains('\n'));
1608		assert!(pretty.contains("greater_than"));
1609	}
1610
1611	#[test]
1612	fn test_access_source() {
1613		let expr = Expression::AccessSource(AccessPrimitiveExpression {
1614			column: ColumnIdentifier {
1615				primitive: ColumnPrimitive::Primitive {
1616					namespace: internal_fragment("public"),
1617					primitive: internal_fragment("users"),
1618				},
1619				name: internal_fragment("email"),
1620			},
1621		});
1622
1623		let json = to_json(&expr);
1624		assert_eq!(json, r#"{"type":"access_source","namespace":"public","source":"users","name":"email"}"#);
1625
1626		let recovered = from_json(&json).unwrap();
1627		assert_eq!(to_json(&recovered), json);
1628	}
1629}