surrealdb_expr/expr/convert/
literal.rs1use 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}