surrealdb_core/fnc/script/
from.rs1use super::classes;
2use crate::sql::array::Array;
3use crate::sql::datetime::Datetime;
4use crate::sql::object::Object;
5use crate::sql::value::Value;
6use crate::sql::Bytes;
7use crate::sql::Geometry;
8use crate::sql::Id;
9use crate::sql::Strand;
10use chrono::{TimeZone, Utc};
11use js::prelude::This;
12use js::Coerced;
13use js::Ctx;
14use js::Error;
15use js::Exception;
16use js::FromAtom;
17use js::FromJs;
18use rust_decimal::Decimal;
19
20fn check_nul(s: &str) -> Result<(), Error> {
21 if s.contains('\0') {
22 Err(Error::InvalidString(std::ffi::CString::new(s).unwrap_err()))
23 } else {
24 Ok(())
25 }
26}
27
28fn try_object_to_geom(object: &Object) -> Option<Geometry> {
29 if object.len() != 2 {
30 return None;
31 }
32
33 let Some(Value::Strand(Strand(key))) = object.get("type") else {
34 return None;
35 };
36
37 match key.as_str() {
38 "Point" => {
39 object.get("coordinates").and_then(Geometry::array_to_point).map(Geometry::Point)
40 }
41 "LineString" => {
42 object.get("coordinates").and_then(Geometry::array_to_line).map(Geometry::Line)
43 }
44 "Polygon" => {
45 object.get("coordinates").and_then(Geometry::array_to_polygon).map(Geometry::Polygon)
46 }
47 "MultiPoint" => object
48 .get("coordinates")
49 .and_then(Geometry::array_to_multipoint)
50 .map(Geometry::MultiPoint),
51 "MultiLineString" => object
52 .get("coordinates")
53 .and_then(Geometry::array_to_multiline)
54 .map(Geometry::MultiLine),
55 "MultiPolygon" => object
56 .get("coordinates")
57 .and_then(Geometry::array_to_multipolygon)
58 .map(Geometry::MultiPolygon),
59 "GeometryCollection" => {
60 let Some(Value::Array(x)) = object.get("geometries") else {
61 return None;
62 };
63
64 let mut res = Vec::with_capacity(x.len());
65
66 for x in x.iter() {
67 let Value::Geometry(x) = x else {
68 return None;
69 };
70 res.push(x.clone());
71 }
72
73 Some(Geometry::Collection(res))
74 }
75
76 _ => None,
77 }
78}
79
80impl<'js> FromJs<'js> for Value {
81 fn from_js(ctx: &Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> {
82 match val.type_of() {
83 js::Type::Undefined => Ok(Value::None),
84 js::Type::Null => Ok(Value::Null),
85 js::Type::Bool => Ok(Value::from(val.as_bool().unwrap())),
86 js::Type::Int => Ok(Value::from(val.as_int().unwrap() as f64)),
87 js::Type::Float => Ok(Value::from(val.as_float().unwrap())),
88 js::Type::String => Ok(Value::from(val.as_string().unwrap().to_string()?)),
89 js::Type::Array => {
90 let v = val.as_array().unwrap();
91 let mut x = Array::with_capacity(v.len());
92 for i in v.iter() {
93 let v = i?;
94 x.push(Value::from_js(ctx, v)?);
95 }
96 Ok(x.into())
97 }
98 js::Type::BigInt => {
99 let str = Coerced::<String>::from_js(ctx, val)?;
101 if let Ok(i) = str.parse::<i64>() {
102 return Ok(Value::from(i));
103 }
104 match str.parse::<Decimal>() {
105 Ok(x) => Ok(Value::from(x)),
106 Err(e) => Err(Exception::from_message(ctx.clone(), &e.to_string())?.throw()),
107 }
108 }
109 js::Type::Object | js::Type::Exception => {
110 let v = val.into_object().unwrap();
112 if v.is_error() {
114 let e: String = v.get(js::atom::PredefinedAtom::Message)?;
115 let (Ok(e) | Err(e)) =
116 Exception::from_message(ctx.clone(), &e).map(|x| x.throw());
117 return Err(e);
118 }
119 if let Some(v) = v.as_class::<classes::record::Record>() {
121 let borrow = v.borrow();
122 let v: &classes::record::Record = &borrow;
123 check_nul(&v.value.tb)?;
124 if let Id::String(s) = &v.value.id {
125 check_nul(s)?;
126 }
127 return Ok(v.value.clone().into());
128 }
129 if let Some(v) = v.as_class::<classes::duration::Duration>() {
131 let borrow = v.borrow();
132 let v: &classes::duration::Duration = &borrow;
133 return match &v.value {
134 Some(v) => Ok((*v).into()),
135 None => Ok(Value::None),
136 };
137 }
138 if let Some(v) = v.as_class::<classes::uuid::Uuid>() {
140 let borrow = v.borrow();
141 let v: &classes::uuid::Uuid = &borrow;
142 return match &v.value {
143 Some(v) => Ok((*v).into()),
144 None => Ok(Value::None),
145 };
146 }
147
148 if let Some(v) = v.as_typed_array::<u8>() {
149 let Some(data) = v.as_bytes() else {
150 return Err(Error::new_from_js_message(
151 "Uint8Array",
152 "Bytes",
153 "Uint8Array data was consumed.",
154 ));
155 };
156
157 return Ok(Value::Bytes(Bytes(data.to_vec())));
158 }
159
160 let date: js::Object = ctx.globals().get(js::atom::PredefinedAtom::Date)?;
162 if (v).is_instance_of(&date) {
163 let f: js::Function = v.get("getTime")?;
164 let m: i64 = f.call((This(v),))?;
165 let d = Utc.timestamp_millis_opt(m).unwrap();
166 return Ok(Datetime::from(d).into());
167 }
168
169 if let Some(v) = v.as_array() {
171 let mut x = Array::with_capacity(v.len());
172 for i in v.iter() {
173 let v = i?;
174 let v = Value::from_js(ctx, v)?;
175 x.push(v);
176 }
177 return Ok(x.into());
178 }
179
180 if v.as_function().is_some() {
182 return Ok(Value::None);
183 }
184 let mut x = Object::default();
186 for i in v.props() {
187 let (k, v) = i?;
188 let k = String::from_atom(k)?;
189 check_nul(&k)?;
190 let v = Value::from_js(ctx, v)?;
191 x.insert(k, v);
192 }
193
194 if let Some(x) = try_object_to_geom(&x) {
195 return Ok(x.into());
196 }
197
198 Ok(x.into())
199 }
200 _ => Ok(Value::Null),
201 }
202 }
203}