surrealdb_core/fnc/script/
into.rs

1use super::classes;
2use crate::sql::geometry::Geometry;
3use crate::sql::number::Number;
4use crate::sql::value::Value;
5use js::Array;
6use js::BigInt;
7use js::Class;
8use js::Ctx;
9use js::Error;
10use js::Exception;
11use js::FromIteratorJs as _;
12use js::IntoJs;
13use js::Null;
14use js::Object;
15use js::TypedArray;
16use js::Undefined;
17use rust_decimal::prelude::ToPrimitive;
18
19const F64_INT_MAX: i64 = ((1u64 << f64::MANTISSA_DIGITS) - 1) as i64;
20const F64_INT_MIN: i64 = -F64_INT_MAX - 1;
21
22impl<'js> IntoJs<'js> for Value {
23	fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> {
24		(&self).into_js(ctx)
25	}
26}
27
28impl<'js> IntoJs<'js> for &Value {
29	fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> {
30		match *self {
31			Value::Null => Null.into_js(ctx),
32			Value::None => Undefined.into_js(ctx),
33			Value::Bool(boolean) => Ok(js::Value::new_bool(ctx.clone(), boolean)),
34			Value::Strand(ref v) => js::String::from_str(ctx.clone(), v)?.into_js(ctx),
35			Value::Number(Number::Int(v)) => {
36				if ((i32::MIN as i64)..=(i32::MAX as i64)).contains(&v) {
37					Ok(js::Value::new_int(ctx.clone(), v as i32))
38				} else if (F64_INT_MIN..=F64_INT_MAX).contains(&v) {
39					Ok(js::Value::new_float(ctx.clone(), v as f64))
40				} else {
41					Ok(js::Value::from(BigInt::from_i64(ctx.clone(), v)?))
42				}
43			}
44			Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx.clone(), v)),
45			Value::Number(Number::Decimal(v)) => {
46				if v.is_integer() {
47					if let Some(v) = v.to_i64() {
48						if ((i32::MIN as i64)..=(i32::MAX as i64)).contains(&v) {
49							Ok(js::Value::new_int(ctx.clone(), v as i32))
50						} else if (F64_INT_MIN..=F64_INT_MAX).contains(&v) {
51							Ok(js::Value::new_float(ctx.clone(), v as f64))
52						} else {
53							Ok(js::Value::from(BigInt::from_i64(ctx.clone(), v)?))
54						}
55					} else {
56						Err(Exception::from_message(
57							ctx.clone(),
58							"Couldn't convert SurrealQL Decimal to JavaScript number",
59						)?
60						.throw())
61					}
62				} else if let Ok(v) = v.try_into() {
63					Ok(js::Value::new_float(ctx.clone(), v))
64				} else {
65					// FIXME: Add support for larger numbers if rquickjs ever adds support.
66					Err(Exception::from_message(
67						ctx.clone(),
68						"Couldn't convert SurrealQL Decimal to a JavaScript number",
69					)?
70					.throw())
71				}
72			}
73			Value::Datetime(ref v) => {
74				let date: js::function::Constructor = ctx.globals().get("Date")?;
75				date.construct((v.0.timestamp_millis(),))
76			}
77			Value::Thing(ref v) => Ok(Class::<classes::record::Record>::instance(
78				ctx.clone(),
79				classes::record::Record {
80					value: v.to_owned(),
81				},
82			)?
83			.into_value()),
84			Value::Duration(ref v) => Ok(Class::<classes::duration::Duration>::instance(
85				ctx.clone(),
86				classes::duration::Duration {
87					value: Some(v.to_owned()),
88				},
89			)?
90			.into_value()),
91			Value::Uuid(ref v) => Ok(Class::<classes::uuid::Uuid>::instance(
92				ctx.clone(),
93				classes::uuid::Uuid {
94					value: Some(v.to_owned()),
95				},
96			)?
97			.into_value()),
98			Value::Array(ref v) => {
99				let x = Array::new(ctx.clone())?;
100				for (i, v) in v.iter().enumerate() {
101					x.set(i, v)?;
102				}
103				x.into_js(ctx)
104			}
105			Value::Object(ref v) => {
106				let x = Object::new(ctx.clone())?;
107				for (k, v) in v.iter() {
108					x.set(k, v)?;
109				}
110				x.into_js(ctx)
111			}
112			Value::Bytes(ref v) => TypedArray::new_copy(ctx.clone(), v.0.as_slice())?.into_js(ctx),
113			Value::Geometry(ref v) => v.into_js(ctx),
114			_ => Undefined.into_js(ctx),
115		}
116	}
117}
118
119impl<'js> IntoJs<'js> for &Geometry {
120	fn into_js(self, ctx: &Ctx<'js>) -> js::Result<js::Value<'js>> {
121		let (ty, coords) = match self {
122			Geometry::Point(x) => {
123				("Point".into_js(ctx)?, Array::from_iter_js(ctx, [x.0.x, x.0.y])?)
124			}
125			Geometry::Line(x) => {
126				let array = Array::new(ctx.clone())?;
127				for (idx, c) in x.0.iter().enumerate() {
128					let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
129					array.set(idx, coord)?;
130				}
131				("LineString".into_js(ctx)?, array)
132			}
133			Geometry::Polygon(x) => {
134				let coords = Array::new(ctx.clone())?;
135
136				let string = Array::new(ctx.clone())?;
137				for (idx, c) in x.exterior().0.iter().enumerate() {
138					let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
139					string.set(idx, coord)?;
140				}
141
142				coords.set(0, string)?;
143
144				for (idx, int) in x.interiors().iter().enumerate() {
145					let string = Array::new(ctx.clone())?;
146					for (idx, c) in int.0.iter().enumerate() {
147						let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
148						string.set(idx, coord)?;
149					}
150					coords.set(idx + 1, string)?;
151				}
152
153				("Polygon".into_js(ctx)?, coords)
154			}
155			Geometry::MultiPoint(x) => {
156				let array = Array::new(ctx.clone())?;
157				for (idx, c) in x.0.iter().enumerate() {
158					let coord = Array::from_iter_js(ctx, [c.x(), c.y()])?;
159					array.set(idx, coord)?;
160				}
161				("MultiPoint".into_js(ctx)?, array)
162			}
163			Geometry::MultiLine(x) => {
164				let lines = Array::new(ctx.clone())?;
165				for (idx, l) in x.0.iter().enumerate() {
166					let array = Array::new(ctx.clone())?;
167					for (idx, c) in l.0.iter().enumerate() {
168						let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
169						array.set(idx, coord)?;
170					}
171					lines.set(idx, array)?
172				}
173				("MultiLineString".into_js(ctx)?, lines)
174			}
175			Geometry::MultiPolygon(x) => {
176				let polygons = Array::new(ctx.clone())?;
177
178				for (idx, p) in x.0.iter().enumerate() {
179					let coords = Array::new(ctx.clone())?;
180
181					let string = Array::new(ctx.clone())?;
182					for (idx, c) in p.exterior().0.iter().enumerate() {
183						let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
184						string.set(idx, coord)?;
185					}
186
187					coords.set(0, string)?;
188
189					for (idx, int) in p.interiors().iter().enumerate() {
190						let string = Array::new(ctx.clone())?;
191						for (idx, c) in int.0.iter().enumerate() {
192							let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
193							string.set(idx, coord)?;
194						}
195						coords.set(idx + 1, string)?;
196					}
197
198					polygons.set(idx, coords)?;
199				}
200				("MultiPolygon".into_js(ctx)?, polygons)
201			}
202			Geometry::Collection(x) => {
203				let geoms = Array::new(ctx.clone())?;
204
205				for (idx, g) in x.iter().enumerate() {
206					let g = g.into_js(ctx)?;
207					geoms.set(idx, g)?;
208				}
209
210				let object = Object::new(ctx.clone())?;
211				object.set("type", "GeometryCollection")?;
212				object.set("geometries", geoms)?;
213				return Ok(object.into_value());
214			}
215		};
216		let object = Object::new(ctx.clone())?;
217		object.set("type", ty)?;
218		object.set("coordinates", coords)?;
219		Ok(object.into_value())
220	}
221}