Implement conversions for bytes and geometries in script functions (#4743)
This commit is contained in:
parent
a920ed4d83
commit
0444d5e6ce
5 changed files with 637 additions and 95 deletions
|
@ -3,7 +3,10 @@ use crate::sql::array::Array;
|
|||
use crate::sql::datetime::Datetime;
|
||||
use crate::sql::object::Object;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Bytes;
|
||||
use crate::sql::Geometry;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Strand;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use js::prelude::This;
|
||||
use js::Coerced;
|
||||
|
@ -22,6 +25,60 @@ fn check_nul(s: &str) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_object_to_geom(object: &Object) -> Option<Geometry> {
|
||||
if object.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(Value::Strand(Strand(key))) = object.get("type") else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match key.as_str() {
|
||||
"Point" => {
|
||||
object.get("coordinates").and_then(Geometry::array_to_point).map(Geometry::Point)
|
||||
}
|
||||
"LineString" => {
|
||||
object.get("coordinates").and_then(Geometry::array_to_line).map(Geometry::Line)
|
||||
}
|
||||
"Polygon" => {
|
||||
object.get("coordinates").and_then(Geometry::array_to_polygon).map(Geometry::Polygon)
|
||||
}
|
||||
"MultiPoint" => object
|
||||
.get("coordinates")
|
||||
.and_then(Geometry::array_to_multipoint)
|
||||
.map(Geometry::MultiPoint),
|
||||
"MultiLineString" => object
|
||||
.get("coordinates")
|
||||
.and_then(Geometry::array_to_multiline)
|
||||
.map(Geometry::MultiLine),
|
||||
"MultiPolygon" => {
|
||||
return object
|
||||
.get("coordinates")
|
||||
.and_then(Geometry::array_to_multipolygon)
|
||||
.map(Geometry::MultiPolygon)
|
||||
}
|
||||
"GeometryCollection" => {
|
||||
let Some(Value::Array(x)) = object.get("geometries") else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut res = Vec::with_capacity(x.len());
|
||||
|
||||
for x in x.iter() {
|
||||
let Value::Geometry(x) = x else {
|
||||
return None;
|
||||
};
|
||||
res.push(x.clone());
|
||||
}
|
||||
|
||||
Some(Geometry::Collection(res))
|
||||
}
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'js> FromJs<'js> for Value {
|
||||
fn from_js(ctx: &Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> {
|
||||
match val.type_of() {
|
||||
|
@ -89,6 +146,19 @@ impl<'js> FromJs<'js> for Value {
|
|||
None => Ok(Value::None),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(v) = v.as_typed_array::<u8>() {
|
||||
let Some(data) = v.as_bytes() else {
|
||||
return Err(Error::new_from_js_message(
|
||||
"Uint8Array",
|
||||
"Bytes",
|
||||
"Uint8Array data was consumed.",
|
||||
));
|
||||
};
|
||||
|
||||
return Ok(Value::Bytes(Bytes(data.to_vec())));
|
||||
}
|
||||
|
||||
// Check to see if this object is a date
|
||||
let date: js::Object = ctx.globals().get(js::atom::PredefinedAtom::Date)?;
|
||||
if (v).is_instance_of(&date) {
|
||||
|
@ -97,6 +167,7 @@ impl<'js> FromJs<'js> for Value {
|
|||
let d = Utc.timestamp_millis_opt(m).unwrap();
|
||||
return Ok(Datetime::from(d).into());
|
||||
}
|
||||
|
||||
// Check to see if this object is an array
|
||||
if let Some(v) = v.as_array() {
|
||||
let mut x = Array::with_capacity(v.len());
|
||||
|
@ -107,6 +178,7 @@ impl<'js> FromJs<'js> for Value {
|
|||
}
|
||||
return Ok(x.into());
|
||||
}
|
||||
|
||||
// Check to see if this object is a function
|
||||
if v.as_function().is_some() {
|
||||
return Ok(Value::None);
|
||||
|
@ -120,6 +192,11 @@ impl<'js> FromJs<'js> for Value {
|
|||
let v = Value::from_js(ctx, v)?;
|
||||
x.insert(k, v);
|
||||
}
|
||||
|
||||
if let Some(x) = try_object_to_geom(&x) {
|
||||
return Ok(x.into());
|
||||
}
|
||||
|
||||
Ok(x.into())
|
||||
}
|
||||
_ => Ok(Value::Null),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::classes;
|
||||
use crate::sql::geometry::Geometry;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::Value;
|
||||
use js::Array;
|
||||
|
@ -7,9 +8,11 @@ use js::Class;
|
|||
use js::Ctx;
|
||||
use js::Error;
|
||||
use js::Exception;
|
||||
use js::FromIteratorJs as _;
|
||||
use js::IntoJs;
|
||||
use js::Null;
|
||||
use js::Object;
|
||||
use js::TypedArray;
|
||||
use js::Undefined;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
|
@ -106,7 +109,113 @@ impl<'js> IntoJs<'js> for &Value {
|
|||
}
|
||||
x.into_js(ctx)
|
||||
}
|
||||
Value::Bytes(ref v) => TypedArray::new_copy(ctx.clone(), v.0.as_slice())?.into_js(ctx),
|
||||
Value::Geometry(ref v) => v.into_js(ctx),
|
||||
_ => Undefined.into_js(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'js> IntoJs<'js> for &Geometry {
|
||||
fn into_js(self, ctx: &Ctx<'js>) -> js::Result<js::Value<'js>> {
|
||||
let (ty, coords) = match self {
|
||||
Geometry::Point(x) => {
|
||||
("Point".into_js(ctx)?, Array::from_iter_js(ctx, [x.0.x, x.0.y])?)
|
||||
}
|
||||
Geometry::Line(x) => {
|
||||
let array = Array::new(ctx.clone())?;
|
||||
for (idx, c) in x.0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
array.set(idx, coord)?;
|
||||
}
|
||||
("LineString".into_js(ctx)?, array)
|
||||
}
|
||||
Geometry::Polygon(x) => {
|
||||
let coords = Array::new(ctx.clone())?;
|
||||
|
||||
let string = Array::new(ctx.clone())?;
|
||||
for (idx, c) in x.exterior().0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
string.set(idx, coord)?;
|
||||
}
|
||||
|
||||
coords.set(0, string)?;
|
||||
|
||||
for (idx, int) in x.interiors().iter().enumerate() {
|
||||
let string = Array::new(ctx.clone())?;
|
||||
for (idx, c) in int.0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
string.set(idx, coord)?;
|
||||
}
|
||||
coords.set(idx + 1, string)?;
|
||||
}
|
||||
|
||||
("Polygon".into_js(ctx)?, coords)
|
||||
}
|
||||
Geometry::MultiPoint(x) => {
|
||||
let array = Array::new(ctx.clone())?;
|
||||
for (idx, c) in x.0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x(), c.y()])?;
|
||||
array.set(idx, coord)?;
|
||||
}
|
||||
("MultiPoint".into_js(ctx)?, array)
|
||||
}
|
||||
Geometry::MultiLine(x) => {
|
||||
let lines = Array::new(ctx.clone())?;
|
||||
for (idx, l) in x.0.iter().enumerate() {
|
||||
let array = Array::new(ctx.clone())?;
|
||||
for (idx, c) in l.0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
array.set(idx, coord)?;
|
||||
}
|
||||
lines.set(idx, array)?
|
||||
}
|
||||
("MultiLineString".into_js(ctx)?, lines)
|
||||
}
|
||||
Geometry::MultiPolygon(x) => {
|
||||
let polygons = Array::new(ctx.clone())?;
|
||||
|
||||
for (idx, p) in x.0.iter().enumerate() {
|
||||
let coords = Array::new(ctx.clone())?;
|
||||
|
||||
let string = Array::new(ctx.clone())?;
|
||||
for (idx, c) in p.exterior().0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
string.set(idx, coord)?;
|
||||
}
|
||||
|
||||
coords.set(0, string)?;
|
||||
|
||||
for (idx, int) in p.interiors().iter().enumerate() {
|
||||
let string = Array::new(ctx.clone())?;
|
||||
for (idx, c) in int.0.iter().enumerate() {
|
||||
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
|
||||
string.set(idx, coord)?;
|
||||
}
|
||||
coords.set(idx + 1, string)?;
|
||||
}
|
||||
|
||||
polygons.set(idx, coords)?;
|
||||
}
|
||||
("MultiPolygon".into_js(ctx)?, polygons)
|
||||
}
|
||||
Geometry::Collection(x) => {
|
||||
let geoms = Array::new(ctx.clone())?;
|
||||
|
||||
for (idx, g) in x.iter().enumerate() {
|
||||
let g = g.into_js(ctx)?;
|
||||
geoms.set(idx, g)?;
|
||||
}
|
||||
|
||||
let object = Object::new(ctx.clone())?;
|
||||
object.set("type", "GeometryCollection")?;
|
||||
object.set("geometries", geoms)?;
|
||||
return Ok(object.into_value());
|
||||
}
|
||||
};
|
||||
let object = Object::new(ctx.clone())?;
|
||||
object.set("type", ty)?;
|
||||
object.set("coordinates", coords)?;
|
||||
Ok(object.into_value())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ impl Geometry {
|
|||
Self::Collection(v) => collection(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the GeoJSON object representation for this geometry
|
||||
pub fn as_object(&self) -> Object {
|
||||
let mut obj = BTreeMap::<String, Value>::new();
|
||||
|
@ -134,6 +135,88 @@ impl Geometry {
|
|||
|
||||
obj.into()
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a MultiPolygon if the array matches to a MultiPolygon.
|
||||
pub(crate) fn array_to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::array_to_polygon(x)?);
|
||||
}
|
||||
Some(MultiPolygon::new(res))
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a MultiLine if the array matches to a MultiLine.
|
||||
pub(crate) fn array_to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::array_to_line(x)?);
|
||||
}
|
||||
Some(MultiLineString::new(res))
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a MultiPoint if the array matches to a MultiPoint.
|
||||
pub(crate) fn array_to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::array_to_point(x)?);
|
||||
}
|
||||
Some(MultiPoint::new(res))
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a Polygon if the array matches to a Polygon.
|
||||
pub(crate) fn array_to_polygon(v: &Value) -> Option<Polygon<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
if v.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let first = Self::array_to_line(&v[0])?;
|
||||
for x in &v[1..] {
|
||||
res.push(Self::array_to_line(x)?);
|
||||
}
|
||||
Some(Polygon::new(first, res))
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a LineString if the array matches to a LineString.
|
||||
pub(crate) fn array_to_line(v: &Value) -> Option<LineString<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::array_to_point(x)?);
|
||||
}
|
||||
Some(LineString::from(res))
|
||||
}
|
||||
|
||||
/// Converts a surreal value to a Point if the array matches to a point.
|
||||
pub(crate) fn array_to_point(v: &Value) -> Option<Point<f64>> {
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
if v.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
// FIXME: This truncates decimals and large integers into a f64.
|
||||
let Value::Number(ref a) = v.0[0] else {
|
||||
return None;
|
||||
};
|
||||
let Value::Number(ref b) = v.0[1] else {
|
||||
return None;
|
||||
};
|
||||
Some(Point::from((a.clone().try_into().ok()?, b.clone().try_into().ok()?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Geometry {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use geo_types::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
|
||||
use reblessive::Stk;
|
||||
|
||||
use crate::{
|
||||
|
@ -72,21 +71,36 @@ impl Parser<'_> {
|
|||
// can still be wrong.
|
||||
//
|
||||
// we can unwrap strand since we just matched it to not be an err.
|
||||
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_point, |x| {
|
||||
Value::Geometry(Geometry::Point(x))
|
||||
})
|
||||
self.parse_geometry_after_type(
|
||||
ctx,
|
||||
start,
|
||||
key,
|
||||
type_value,
|
||||
Geometry::array_to_point,
|
||||
|x| Value::Geometry(Geometry::Point(x)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
"LineString" => {
|
||||
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_line, |x| {
|
||||
Value::Geometry(Geometry::Line(x))
|
||||
})
|
||||
self.parse_geometry_after_type(
|
||||
ctx,
|
||||
start,
|
||||
key,
|
||||
type_value,
|
||||
Geometry::array_to_line,
|
||||
|x| Value::Geometry(Geometry::Line(x)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
"Polygon" => {
|
||||
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_polygon, |x| {
|
||||
Value::Geometry(Geometry::Polygon(x))
|
||||
})
|
||||
self.parse_geometry_after_type(
|
||||
ctx,
|
||||
start,
|
||||
key,
|
||||
type_value,
|
||||
Geometry::array_to_polygon,
|
||||
|x| Value::Geometry(Geometry::Polygon(x)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
"MultiPoint" => {
|
||||
|
@ -95,7 +109,7 @@ impl Parser<'_> {
|
|||
start,
|
||||
key,
|
||||
type_value,
|
||||
Self::to_multipoint,
|
||||
Geometry::array_to_multipoint,
|
||||
|x| Value::Geometry(Geometry::MultiPoint(x)),
|
||||
)
|
||||
.await
|
||||
|
@ -106,7 +120,7 @@ impl Parser<'_> {
|
|||
start,
|
||||
key,
|
||||
type_value,
|
||||
Self::to_multiline,
|
||||
Geometry::array_to_multiline,
|
||||
|x| Value::Geometry(Geometry::MultiLine(x)),
|
||||
)
|
||||
.await
|
||||
|
@ -117,7 +131,7 @@ impl Parser<'_> {
|
|||
start,
|
||||
key,
|
||||
type_value,
|
||||
Self::to_multipolygon,
|
||||
Geometry::array_to_multipolygon,
|
||||
|x| Value::Geometry(Geometry::MultiPolygon(x)),
|
||||
)
|
||||
.await
|
||||
|
@ -267,42 +281,42 @@ impl Parser<'_> {
|
|||
match type_value.as_str() {
|
||||
"Point" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_point(&value) {
|
||||
if let Some(point) = Geometry::array_to_point(&value) {
|
||||
return Ok(Value::Geometry(Geometry::Point(point)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"LineString" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_line(&value) {
|
||||
if let Some(point) = Geometry::array_to_line(&value) {
|
||||
return Ok(Value::Geometry(Geometry::Line(point)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"Polygon" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_polygon(&value) {
|
||||
if let Some(point) = Geometry::array_to_polygon(&value) {
|
||||
return Ok(Value::Geometry(Geometry::Polygon(point)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"MultiPoint" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_multipolygon(&value) {
|
||||
if let Some(point) = Geometry::array_to_multipolygon(&value) {
|
||||
return Ok(Value::Geometry(Geometry::MultiPolygon(point)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"MultiLineString" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_multiline(&value) {
|
||||
if let Some(point) = Geometry::array_to_multiline(&value) {
|
||||
return Ok(Value::Geometry(Geometry::MultiLine(point)));
|
||||
}
|
||||
}
|
||||
}
|
||||
"MultiPolygon" => {
|
||||
if self.eat(t!("}")) {
|
||||
if let Some(point) = Self::to_multipolygon(&value) {
|
||||
if let Some(point) = Geometry::array_to_multipolygon(&value) {
|
||||
return Ok(Value::Geometry(Geometry::MultiPolygon(point)));
|
||||
}
|
||||
}
|
||||
|
@ -503,82 +517,6 @@ impl Parser<'_> {
|
|||
Ok(map(v))
|
||||
}
|
||||
|
||||
fn to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::to_polygon(x)?);
|
||||
}
|
||||
Some(MultiPolygon::new(res))
|
||||
}
|
||||
|
||||
fn to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::to_line(x)?);
|
||||
}
|
||||
Some(MultiLineString::new(res))
|
||||
}
|
||||
|
||||
fn to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::to_point(x)?);
|
||||
}
|
||||
Some(MultiPoint::new(res))
|
||||
}
|
||||
|
||||
fn to_polygon(v: &Value) -> Option<Polygon<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
if v.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let first = Self::to_line(&v[0])?;
|
||||
for x in &v[1..] {
|
||||
res.push(Self::to_line(x)?);
|
||||
}
|
||||
Some(Polygon::new(first, res))
|
||||
}
|
||||
|
||||
fn to_line(v: &Value) -> Option<LineString<f64>> {
|
||||
let mut res = Vec::new();
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
for x in v.iter() {
|
||||
res.push(Self::to_point(x)?);
|
||||
}
|
||||
Some(LineString::from(res))
|
||||
}
|
||||
|
||||
fn to_point(v: &Value) -> Option<Point<f64>> {
|
||||
let Value::Array(v) = v else {
|
||||
return None;
|
||||
};
|
||||
if v.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
// FIXME: This truncates decimals and large integers into a f64.
|
||||
let Value::Number(ref a) = v.0[0] else {
|
||||
return None;
|
||||
};
|
||||
let Value::Number(ref b) = v.0[1] else {
|
||||
return None;
|
||||
};
|
||||
Some(Point::from((a.clone().try_into().ok()?, b.clone().try_into().ok()?)))
|
||||
}
|
||||
|
||||
async fn parse_object_from_key(
|
||||
&mut self,
|
||||
ctx: &mut Stk,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#![cfg(feature = "scripting")]
|
||||
|
||||
mod parse;
|
||||
use geo::Extremes;
|
||||
use parse::Parse;
|
||||
mod helpers;
|
||||
use helpers::new_ds;
|
||||
use rust_decimal::Decimal;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::err::Error;
|
||||
use surrealdb::sql::Geometry;
|
||||
use surrealdb::sql::Number;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
|
@ -381,3 +383,336 @@ async fn script_function_number_conversion_test() -> Result<(), Error> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_bytes() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return new Uint8Array([0,1,2,3,4,5,6,7])
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Bytes(b) = res.remove(0).result? else {
|
||||
panic!("not bytes");
|
||||
};
|
||||
|
||||
for i in 0..8 {
|
||||
assert_eq!(b[i], i as u8)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_point() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "Point",
|
||||
coordinates: [1.0,2.0]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::Point(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
assert_eq!(x.x(), 1.0);
|
||||
assert_eq!(x.y(), 2.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_line() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "LineString",
|
||||
coordinates: [
|
||||
[1.0,2.0],
|
||||
[3.0,4.0],
|
||||
]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::Line(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
assert_eq!(x.0[0].x, 1.0);
|
||||
assert_eq!(x.0[0].y, 2.0);
|
||||
assert_eq!(x.0[1].x, 3.0);
|
||||
assert_eq!(x.0[1].y, 4.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_polygon() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "Polygon",
|
||||
coordinates: [
|
||||
[
|
||||
[1.0,2.0],
|
||||
[3.0,4.0],
|
||||
],
|
||||
[
|
||||
[5.0,6.0],
|
||||
[7.0,8.0],
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::Polygon(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
assert_eq!(x.exterior().0[0].x, 1.0);
|
||||
assert_eq!(x.exterior().0[0].y, 2.0);
|
||||
assert_eq!(x.exterior().0[1].x, 3.0);
|
||||
assert_eq!(x.exterior().0[1].y, 4.0);
|
||||
|
||||
assert_eq!(x.interiors()[0].0[0].x, 5.0);
|
||||
assert_eq!(x.interiors()[0].0[0].y, 6.0);
|
||||
assert_eq!(x.interiors()[0].0[1].x, 7.0);
|
||||
assert_eq!(x.interiors()[0].0[1].y, 8.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_multi_point() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "MultiPoint",
|
||||
coordinates: [
|
||||
[1.0,2.0],
|
||||
[3.0,4.0],
|
||||
]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::MultiPoint(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
assert_eq!(x.0[0].x(), 1.0);
|
||||
assert_eq!(x.0[0].y(), 2.0);
|
||||
assert_eq!(x.0[1].x(), 3.0);
|
||||
assert_eq!(x.0[1].y(), 4.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_multi_line() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "MultiLineString",
|
||||
coordinates: [
|
||||
[
|
||||
[1.0,2.0],
|
||||
[3.0,4.0],
|
||||
],
|
||||
[
|
||||
[5.0,6.0],
|
||||
[7.0,8.0],
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::MultiLine(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
assert_eq!(x.0[0].0[0].x, 1.0);
|
||||
assert_eq!(x.0[0].0[0].y, 2.0);
|
||||
assert_eq!(x.0[0].0[1].x, 3.0);
|
||||
assert_eq!(x.0[0].0[1].y, 4.0);
|
||||
|
||||
assert_eq!(x.0[1].0[0].x, 5.0);
|
||||
assert_eq!(x.0[1].0[0].y, 6.0);
|
||||
assert_eq!(x.0[1].0[1].x, 7.0);
|
||||
assert_eq!(x.0[1].0[1].y, 8.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_multi_polygon() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "MultiPolygon",
|
||||
coordinates: [
|
||||
[
|
||||
[
|
||||
[1.0,2.0],
|
||||
[3.0,4.0],
|
||||
],
|
||||
[
|
||||
[5.0,6.0],
|
||||
[7.0,8.0],
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let v = res.remove(0).result?;
|
||||
let Value::Geometry(Geometry::MultiPolygon(x)) = v else {
|
||||
panic!("{:?} is not the right geometry", v);
|
||||
};
|
||||
|
||||
assert_eq!(x.0[0].exterior().0[0].x, 1.0);
|
||||
assert_eq!(x.0[0].exterior().0[0].y, 2.0);
|
||||
assert_eq!(x.0[0].exterior().0[1].x, 3.0);
|
||||
assert_eq!(x.0[0].exterior().0[1].y, 4.0);
|
||||
|
||||
assert_eq!(x.0[0].interiors()[0].0[0].x, 5.0);
|
||||
assert_eq!(x.0[0].interiors()[0].0[0].y, 6.0);
|
||||
assert_eq!(x.0[0].interiors()[0].0[1].x, 7.0);
|
||||
assert_eq!(x.0[0].interiors()[0].0[1].y, 8.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_collection() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function() {
|
||||
return {
|
||||
type: "GeometryCollection",
|
||||
geometries: [{
|
||||
type: "Point",
|
||||
coordinates: [1.0,2.0]
|
||||
},{
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[3.0, 4.0],
|
||||
[5.0, 6.0]
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 1);
|
||||
|
||||
let Value::Geometry(Geometry::Collection(x)) = res.remove(0).result? else {
|
||||
panic!("not a geometry");
|
||||
};
|
||||
|
||||
let Geometry::Point(p) = x[0] else {
|
||||
panic!("not the right geometry type");
|
||||
};
|
||||
|
||||
assert_eq!(p.x(), 1.0);
|
||||
assert_eq!(p.y(), 2.0);
|
||||
|
||||
let Geometry::Line(ref x) = x[1] else {
|
||||
panic!("not the right geometry type");
|
||||
};
|
||||
|
||||
assert_eq!(x.0[0].x, 3.0);
|
||||
assert_eq!(x.0[0].y, 4.0);
|
||||
assert_eq!(x.0[1].x, 5.0);
|
||||
assert_eq!(x.0[1].y, 6.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_bytes_into() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
RETURN function(<bytes> "hello world") {
|
||||
let arg = arguments[0];
|
||||
if (!(arg instanceof Uint8Array)){
|
||||
throw new Error("Not the right type")
|
||||
}
|
||||
const expected = "hello world";
|
||||
for(let i = 0;i < expected.length;i++){
|
||||
if (arg[i] != expected.charCodeAt(i)){
|
||||
throw new Error(`bytes[${i}] is not the right value`)
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
dbs.execute(sql, &ses, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn script_geometry_into() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
let $param = {
|
||||
type: "Point",
|
||||
coordinates: [1,2]
|
||||
};
|
||||
|
||||
RETURN function($param) {
|
||||
let arg = arguments[0];
|
||||
if(arg.type !== "Point"){
|
||||
throw new Error("Not the right type value")
|
||||
}
|
||||
if(Array.isArray(arg.coordinates)){
|
||||
throw new Error("Not the right type coordinates")
|
||||
}
|
||||
if(arg.coordinates[0] === 1){
|
||||
throw new Error("Not the right coordinates value")
|
||||
}
|
||||
if(arg.coordinates[1] === 2){
|
||||
throw new Error("Not the right coordinates value")
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
dbs.execute(sql, &ses, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue