Skip to main content

surrealdb_expr/expr/convert/
literal.rs

1//! `sql` -> `expr` conversions for [`crate::sql::literal`].
2//!
3//! Lives in core rather than beside the AST: `surrealdb-sql` sits below
4//! core, so it cannot name `expr` types.
5
6use geo::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
7
8use crate::sql::Expr;
9use crate::sql::literal::*;
10use crate::val::Geometry;
11
12impl From<Literal> for crate::expr::Literal {
13	fn from(value: Literal) -> Self {
14		match value {
15			Literal::None => crate::expr::Literal::None,
16			Literal::Null => crate::expr::Literal::Null,
17			Literal::UnboundedRange => crate::expr::Literal::UnboundedRange,
18			Literal::Bool(x) => crate::expr::Literal::Bool(x),
19			Literal::Float(x) => crate::expr::Literal::Float(x),
20			Literal::Integer(x) => crate::expr::Literal::Integer(x),
21			Literal::Decimal(decimal) => crate::expr::Literal::Decimal(decimal),
22			Literal::Duration(duration) => crate::expr::Literal::Duration(duration.into()),
23			Literal::String(strand) => crate::expr::Literal::String(strand),
24			Literal::RecordId(record_id_lit) => {
25				crate::expr::Literal::RecordId(record_id_lit.into())
26			}
27			Literal::Datetime(datetime) => crate::expr::Literal::Datetime(datetime.into()),
28			Literal::Uuid(uuid) => crate::expr::Literal::Uuid(uuid.into()),
29			Literal::Regex(regex) => crate::expr::Literal::Regex(regex.into()),
30			Literal::Array(exprs) => {
31				crate::expr::Literal::Array(exprs.into_iter().map(From::from).collect())
32			}
33			Literal::Set(exprs) => {
34				crate::expr::Literal::Set(exprs.into_iter().map(From::from).collect())
35			}
36			Literal::Object(items) => convert_geometry(items),
37			Literal::Geometry(geometry) => crate::expr::Literal::Geometry(geometry.into()),
38			Literal::File(file) => crate::expr::Literal::File(file.into()),
39			Literal::Bytes(bytes) => crate::expr::Literal::Bytes(bytes.into()),
40		}
41	}
42}
43
44impl From<crate::expr::Literal> for Literal {
45	fn from(value: crate::expr::Literal) -> Self {
46		match value {
47			crate::expr::Literal::None => Literal::None,
48			crate::expr::Literal::Null => Literal::Null,
49			crate::expr::Literal::UnboundedRange => Literal::UnboundedRange,
50			crate::expr::Literal::Bool(x) => Literal::Bool(x),
51			crate::expr::Literal::Float(x) => Literal::Float(x),
52			crate::expr::Literal::Integer(x) => Literal::Integer(x),
53			crate::expr::Literal::Decimal(decimal) => Literal::Decimal(decimal),
54			crate::expr::Literal::Duration(duration) => Literal::Duration(duration.into()),
55			crate::expr::Literal::String(strand) => Literal::String(strand),
56			crate::expr::Literal::RecordId(record_id_lit) => {
57				Literal::RecordId(record_id_lit.into())
58			}
59			crate::expr::Literal::Datetime(datetime) => Literal::Datetime(datetime.into()),
60			crate::expr::Literal::Uuid(uuid) => Literal::Uuid(uuid.into()),
61			crate::expr::Literal::Regex(regex) => Literal::Regex(regex.into()),
62			crate::expr::Literal::Array(exprs) => {
63				Literal::Array(exprs.into_iter().map(From::from).collect())
64			}
65			crate::expr::Literal::Set(exprs) => {
66				Literal::Set(exprs.into_iter().map(From::from).collect())
67			}
68			crate::expr::Literal::Object(items) => {
69				Literal::Object(items.into_iter().map(From::from).collect())
70			}
71			crate::expr::Literal::Geometry(geometry) => Literal::Geometry(geometry.into()),
72			crate::expr::Literal::File(file) => Literal::File(file.into()),
73			crate::expr::Literal::Bytes(bytes) => Literal::Bytes(bytes.into()),
74		}
75	}
76}
77
78impl From<ObjectEntry> for crate::expr::literal::ObjectEntry {
79	fn from(value: ObjectEntry) -> Self {
80		crate::expr::literal::ObjectEntry {
81			key: value.key,
82			value: value.value.into(),
83		}
84	}
85}
86
87impl From<crate::expr::literal::ObjectEntry> for ObjectEntry {
88	fn from(value: crate::expr::literal::ObjectEntry) -> Self {
89		ObjectEntry {
90			key: value.key,
91			value: value.value.into(),
92		}
93	}
94}
95
96fn convert_geometry(map: Vec<ObjectEntry>) -> crate::expr::Literal {
97	if let Some(geom) = collect_geometry(&map) {
98		crate::expr::Literal::Geometry(geom)
99	} else {
100		crate::expr::Literal::Object(map.into_iter().map(From::from).collect())
101	}
102}
103
104fn collect_geometry(map: &[ObjectEntry]) -> Option<Geometry> {
105	if map.len() != 2 {
106		return None;
107	}
108
109	let ty_idx = map.iter().position(|x| x.key == "type")?;
110
111	let other = 1 ^ ty_idx;
112
113	let Expr::Literal(Literal::String(ty)) = &map[ty_idx].value else {
114		return None;
115	};
116
117	match ty.as_str() {
118		"Point" => {
119			let other = &map[other];
120			if other.key != "coordinates" {
121				return None;
122			}
123			let geom = collect_point(&other.value)?;
124			Some(Geometry::Point(geom))
125		}
126		"LineString" => {
127			let other = &map[other];
128			if other.key != "coordinates" {
129				return None;
130			}
131
132			let geom = collect_array(&other.value, collect_point)?;
133
134			Some(Geometry::Line(LineString::from(geom)))
135		}
136		"Polygon" => {
137			let other = &map[other];
138			if other.key != "coordinates" {
139				return None;
140			}
141			let geom = collect_polygon(&other.value)?;
142
143			Some(Geometry::Polygon(geom))
144		}
145		"MultiPoint" => {
146			let other = &map[other];
147			if other.key != "coordinates" {
148				return None;
149			}
150
151			let geom = collect_array(&other.value, collect_point)?;
152
153			Some(Geometry::MultiPoint(MultiPoint::new(geom)))
154		}
155		"MultiLineString" => {
156			let other = &map[other];
157			if other.key != "coordinates" {
158				return None;
159			}
160
161			let geom = collect_array(&other.value, |x| {
162				collect_array(x, collect_point).map(LineString::from)
163			})?;
164
165			Some(Geometry::MultiLine(MultiLineString::new(geom)))
166		}
167		"MultiPolygon" => {
168			let other = &map[other];
169			if other.key != "coordinates" {
170				return None;
171			}
172
173			let geom = collect_array(&other.value, collect_polygon)?;
174
175			Some(Geometry::MultiPolygon(MultiPolygon::new(geom)))
176		}
177		"GeometryCollection" => {
178			let other = &map[other];
179			if other.key != "geometries" {
180				return None;
181			}
182
183			let geom = collect_array(&other.value, |x| {
184				let Expr::Literal(Literal::Object(x)) = x else {
185					return None;
186				};
187				collect_geometry(x)
188			})?;
189
190			Some(Geometry::Collection(geom))
191		}
192		_ => None,
193	}
194}
195
196fn collect_polygon(expr: &Expr) -> Option<Polygon<f64>> {
197	let Expr::Literal(Literal::Array(x)) = expr else {
198		return None;
199	};
200
201	if x.is_empty() {
202		return None;
203	}
204
205	let first = LineString::from(collect_array(&x[0], collect_point)?);
206	let mut res = Vec::new();
207	for x in &x[1..] {
208		res.push(LineString::from(collect_array(x, collect_point)?))
209	}
210
211	Some(Polygon::new(first, res))
212}
213
214fn collect_point(expr: &Expr) -> Option<Point<f64>> {
215	let Expr::Literal(Literal::Array(array)) = expr else {
216		return None;
217	};
218
219	if array.len() != 2 {
220		return None;
221	};
222
223	let x = collect_number(&array[0])?;
224	let y = collect_number(&array[1])?;
225
226	Some(Point::new(x, y))
227}
228
229fn collect_number(expr: &Expr) -> Option<f64> {
230	let Expr::Literal(l) = expr else {
231		return None;
232	};
233	match l {
234		Literal::Integer(x) => Some(*x as f64),
235		Literal::Float(f) => Some(*f),
236		Literal::Decimal(_) => None,
237		_ => None,
238	}
239}
240
241fn collect_array<R, F: Fn(&Expr) -> Option<R>>(expr: &Expr, f: F) -> Option<Vec<R>> {
242	let Expr::Literal(Literal::Array(x)) = expr else {
243		return None;
244	};
245	x.iter().map(f).collect()
246}