Support tables + geometries in CBOR (#3604)
Co-authored-by: Gerard Guillemas Martos <gerard.guillemas@surrealdb.com>
This commit is contained in:
parent
4eba4a61ab
commit
e5c63234ca
5 changed files with 255 additions and 13 deletions
70
Cargo.lock
generated
70
Cargo.lock
generated
|
@ -2171,7 +2171,7 @@ dependencies = [
|
|||
"log",
|
||||
"num-traits",
|
||||
"robust",
|
||||
"rstar",
|
||||
"rstar 0.11.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -2188,21 +2188,39 @@ dependencies = [
|
|||
"log",
|
||||
"num-traits",
|
||||
"robust",
|
||||
"rstar",
|
||||
"rstar 0.11.0",
|
||||
"serde",
|
||||
"spade",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geo-types"
|
||||
version = "0.7.12"
|
||||
name = "geo"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567495020b114f1ce9bed679b29975aa0bfae06ac22beacd5cfde5dabe7b05d6"
|
||||
checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501"
|
||||
dependencies = [
|
||||
"earcutr",
|
||||
"float_next_after",
|
||||
"geo-types",
|
||||
"geographiclib-rs",
|
||||
"log",
|
||||
"num-traits",
|
||||
"robust",
|
||||
"rstar 0.12.0",
|
||||
"spade",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geo-types"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"arbitrary",
|
||||
"num-traits",
|
||||
"rstar",
|
||||
"rstar 0.11.0",
|
||||
"rstar 0.12.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -2332,6 +2350,15 @@ dependencies = [
|
|||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
|
@ -2382,12 +2409,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"hash32 0.2.1",
|
||||
"rustc_version",
|
||||
"spin 0.9.8",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32 0.3.1",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
|
@ -4040,7 +4077,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
|
@ -4074,7 +4111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
|
@ -4699,7 +4736,18 @@ version = "0.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"heapless 0.7.17",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstar"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
|
||||
dependencies = [
|
||||
"heapless 0.8.0",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
]
|
||||
|
@ -5371,6 +5419,8 @@ dependencies = [
|
|||
"env_logger",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"geo 0.28.0",
|
||||
"geo-types",
|
||||
"glob",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
|
|
|
@ -53,6 +53,8 @@ clap = { version = "4.4.11", features = [
|
|||
] }
|
||||
futures = "0.3.29"
|
||||
futures-util = "0.3.29"
|
||||
geo = "0.28.0"
|
||||
geo-types = "0.7.13"
|
||||
glob = "0.3.1"
|
||||
http = "0.2.11"
|
||||
http-body = "0.4.5"
|
||||
|
|
|
@ -732,6 +732,10 @@ allow_unsafe = true
|
|||
|
||||
[pkg.heapless]
|
||||
allow_unsafe = true
|
||||
build.allow_apis = [
|
||||
"fs",
|
||||
"process",
|
||||
]
|
||||
|
||||
[pkg.vcpkg]
|
||||
from.build.allow_apis = [
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
use ciborium::Value as Data;
|
||||
use geo::{LineString, Point, Polygon};
|
||||
use geo_types::{MultiLineString, MultiPoint, MultiPolygon};
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter::once;
|
||||
use std::ops::Deref;
|
||||
use surrealdb::sql::Datetime;
|
||||
use surrealdb::sql::Duration;
|
||||
use surrealdb::sql::Geometry;
|
||||
use surrealdb::sql::Id;
|
||||
use surrealdb::sql::Number;
|
||||
use surrealdb::sql::Thing;
|
||||
|
@ -14,6 +19,14 @@ const TAG_UUID: u64 = 7;
|
|||
const TAG_DECIMAL: u64 = 8;
|
||||
const TAG_DURATION: u64 = 9;
|
||||
const TAG_RECORDID: u64 = 10;
|
||||
const TAG_TABLE: u64 = 11;
|
||||
const TAG_GEOMETRY_POINT: u64 = 88;
|
||||
const TAG_GEOMETRY_LINE: u64 = 89;
|
||||
const TAG_GEOMETRY_POLYGON: u64 = 90;
|
||||
const TAG_GEOMETRY_MULTIPOINT: u64 = 91;
|
||||
const TAG_GEOMETRY_MULTILINE: u64 = 92;
|
||||
const TAG_GEOMETRY_MULTIPOLYGON: u64 = 93;
|
||||
const TAG_GEOMETRY_COLLECTION: u64 = 94;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cbor(pub Data);
|
||||
|
@ -113,6 +126,114 @@ impl TryFrom<Cbor> for Value {
|
|||
},
|
||||
_ => Err("Expected a CBOR text data type, or a CBOR array with 2 elements"),
|
||||
},
|
||||
// A literal table
|
||||
TAG_TABLE => match *v {
|
||||
Data::Text(v) => Ok(Value::Table(v.into())),
|
||||
_ => Err("Expected a CBOR text data type"),
|
||||
},
|
||||
TAG_GEOMETRY_POINT => match v.deref() {
|
||||
Data::Array(v) if v.len() == 2 => {
|
||||
let x = Value::try_from(Cbor(v.clone().remove(0)))?;
|
||||
let y = Value::try_from(Cbor(v.clone().remove(0)))?;
|
||||
|
||||
match (x, y) {
|
||||
(Value::Number(x), Value::Number(y)) => {
|
||||
Ok(Value::Geometry(Geometry::Point((x.as_float(), y.as_float()).into())))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with 2 decimal values"),
|
||||
}
|
||||
},
|
||||
_ => Err("Expected a CBOR array with 2 decimal values"),
|
||||
},
|
||||
TAG_GEOMETRY_LINE => match v.deref() {
|
||||
Data::Array(v) => {
|
||||
let points = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(Geometry::Point(v)) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry Point values")
|
||||
})
|
||||
.collect::<Result<Vec<Point>, &str>>()?;
|
||||
|
||||
Ok(Value::Geometry(Geometry::Line(LineString::from(points))))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with Geometry Point values"),
|
||||
},
|
||||
TAG_GEOMETRY_POLYGON => match v.deref() {
|
||||
Data::Array(v) if v.len() >= 2 => {
|
||||
let lines = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(Geometry::Line(v)) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry Line values")
|
||||
})
|
||||
.collect::<Result<Vec<LineString>, &str>>()?;
|
||||
|
||||
let first = match lines.first() {
|
||||
Some(v) => v,
|
||||
_ => return Err("Expected a CBOR array with at least two Geometry Line values")
|
||||
};
|
||||
|
||||
Ok(Value::Geometry(Geometry::Polygon(Polygon::new(first.clone(), Vec::from(&lines[1..])))))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with at least two Geometry Line values"),
|
||||
},
|
||||
TAG_GEOMETRY_MULTIPOINT => match v.deref() {
|
||||
Data::Array(v) => {
|
||||
let points = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(Geometry::Point(v)) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry Point values")
|
||||
})
|
||||
.collect::<Result<Vec<Point>, &str>>()?;
|
||||
|
||||
Ok(Value::Geometry(Geometry::MultiPoint(MultiPoint::from(points))))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with Geometry Point values"),
|
||||
},
|
||||
TAG_GEOMETRY_MULTILINE => match v.deref() {
|
||||
Data::Array(v) => {
|
||||
let lines = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(Geometry::Line(v)) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry Line values")
|
||||
})
|
||||
.collect::<Result<Vec<LineString>, &str>>()?;
|
||||
|
||||
Ok(Value::Geometry(Geometry::MultiLine(MultiLineString::new(lines))))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with Geometry Point values"),
|
||||
},
|
||||
TAG_GEOMETRY_MULTIPOLYGON => match v.deref() {
|
||||
Data::Array(v) => {
|
||||
let polygons = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(Geometry::Polygon(v)) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry Polygon values")
|
||||
})
|
||||
.collect::<Result<Vec<Polygon>, &str>>()?;
|
||||
|
||||
Ok(Value::Geometry(Geometry::MultiPolygon(MultiPolygon::from(polygons))))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with Geometry Polygon values"),
|
||||
},
|
||||
TAG_GEOMETRY_COLLECTION => match v.deref() {
|
||||
Data::Array(v) => {
|
||||
let geometries = v
|
||||
.iter()
|
||||
.map(|v| match Value::try_from(Cbor(v.clone()))? {
|
||||
Value::Geometry(v) => Ok(v),
|
||||
_ => Err("Expected a CBOR array with Geometry values")
|
||||
})
|
||||
.collect::<Result<Vec<Geometry>, &str>>()?;
|
||||
|
||||
Ok(Value::Geometry(Geometry::Collection(geometries)))
|
||||
},
|
||||
_ => Err("Expected a CBOR array with Geometry values"),
|
||||
},
|
||||
// An unknown tag
|
||||
_ => Err("Encountered an unknown CBOR tag"),
|
||||
}
|
||||
|
@ -171,12 +292,61 @@ impl TryFrom<Value> for Cbor {
|
|||
Id::String(v) => Data::Text(v),
|
||||
Id::Array(v) => Cbor::try_from(Value::from(v))?.0,
|
||||
Id::Object(v) => Cbor::try_from(Value::from(v))?.0,
|
||||
Id::Generate(_) => unreachable!(),
|
||||
Id::Generate(_) => {
|
||||
return Err("Cannot encode an ungenerated Record ID into CBOR")
|
||||
}
|
||||
},
|
||||
])),
|
||||
))),
|
||||
Value::Table(v) => Ok(Cbor(Data::Tag(TAG_TABLE, Box::new(Data::Text(v.0))))),
|
||||
Value::Geometry(v) => Ok(Cbor(encode_geometry(v))),
|
||||
// We shouldn't reach here
|
||||
_ => unreachable!(),
|
||||
_ => Err("Found unsupported SurrealQL value being encoded into a CBOR value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_geometry(v: Geometry) -> Data {
|
||||
match v {
|
||||
Geometry::Point(v) => Data::Tag(
|
||||
TAG_GEOMETRY_POINT,
|
||||
Box::new(Data::Array(vec![
|
||||
Data::Tag(TAG_DECIMAL, Box::new(Data::Text(v.x().to_string()))),
|
||||
Data::Tag(TAG_DECIMAL, Box::new(Data::Text(v.y().to_string()))),
|
||||
])),
|
||||
),
|
||||
Geometry::Line(v) => {
|
||||
let data = v.points().map(|v| encode_geometry(v.into())).collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_LINE, Box::new(Data::Array(data)))
|
||||
}
|
||||
Geometry::Polygon(v) => {
|
||||
let data = once(v.exterior())
|
||||
.chain(v.interiors())
|
||||
.map(|v| encode_geometry(v.clone().into()))
|
||||
.collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_POLYGON, Box::new(Data::Array(data)))
|
||||
}
|
||||
Geometry::MultiPoint(v) => {
|
||||
let data = v.iter().map(|v| encode_geometry((*v).into())).collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_MULTIPOINT, Box::new(Data::Array(data)))
|
||||
}
|
||||
Geometry::MultiLine(v) => {
|
||||
let data = v.iter().map(|v| encode_geometry(v.clone().into())).collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_MULTILINE, Box::new(Data::Array(data)))
|
||||
}
|
||||
Geometry::MultiPolygon(v) => {
|
||||
let data = v.iter().map(|v| encode_geometry(v.clone().into())).collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_MULTIPOLYGON, Box::new(Data::Array(data)))
|
||||
}
|
||||
Geometry::Collection(v) => {
|
||||
let data = v.iter().map(|v| encode_geometry(v.clone())).collect::<Vec<Data>>();
|
||||
|
||||
Data::Tag(TAG_GEOMETRY_COLLECTION, Box::new(Data::Array(data)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -663,8 +663,12 @@ criteria = "safe-to-deploy"
|
|||
version = "0.27.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.geo]]
|
||||
version = "0.28.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.geo-types]]
|
||||
version = "0.7.12"
|
||||
version = "0.7.13"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.geographiclib-rs]]
|
||||
|
@ -707,6 +711,10 @@ criteria = "safe-to-deploy"
|
|||
version = "0.2.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.hash32]]
|
||||
version = "0.3.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.hashbrown]]
|
||||
version = "0.14.3"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -719,6 +727,10 @@ criteria = "safe-to-deploy"
|
|||
version = "0.7.17"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.heapless]]
|
||||
version = "0.8.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.hermit-abi]]
|
||||
version = "0.3.5"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -1415,6 +1427,10 @@ criteria = "safe-to-deploy"
|
|||
version = "0.11.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.rstar]]
|
||||
version = "0.12.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.rust-stemmers]]
|
||||
version = "1.2.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
Loading…
Reference in a new issue