Large update to code to convert Golang to Rust
This commit is contained in:
parent
4f4793975e
commit
6e031110bb
94 changed files with 7039 additions and 2688 deletions
1661
Cargo.lock
generated
1661
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
58
Cargo.toml
58
Cargo.toml
|
@ -1,30 +1,46 @@
|
|||
[package]
|
||||
name = "surreal"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
version = "0.0.0"
|
||||
authors = ["Tobie Morgan Hitchcock <tobie@surrealdb.com>"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.40"
|
||||
bytes = "1.0.1"
|
||||
anyhow = "1.0.44"
|
||||
argon2 = "0.3.1"
|
||||
byteorder = "1.4.3"
|
||||
bytes = "1.1.0"
|
||||
clap = "2.33.3"
|
||||
futures = "0.3.13"
|
||||
http = "0.2.3"
|
||||
futures = "0.3.17"
|
||||
fuzzy-matcher = "0.3.7"
|
||||
http = "0.2.5"
|
||||
log = "0.4.14"
|
||||
maplit = "1.0.2"
|
||||
nom = "6.1.2"
|
||||
regex = "1.4.5"
|
||||
thiserror = "1.0.24"
|
||||
wasm-bindgen = "0.2.72"
|
||||
md-5 = "0.9.1"
|
||||
nom = "7.0.0"
|
||||
once_cell = "1.8.0"
|
||||
pbkdf2 = "0.9.0"
|
||||
rand = "0.8.4"
|
||||
regex = "1.5.4"
|
||||
scrypt = "0.8.0"
|
||||
sha-1 = "0.9.8"
|
||||
sha2 = "0.9.8"
|
||||
slug = "0.1.4"
|
||||
thiserror = "1.0.29"
|
||||
url = "2.2.2"
|
||||
utf-8 = "0.7.6"
|
||||
xid = "1.0.0"
|
||||
|
||||
[dependencies.echodb]
|
||||
path = "../echodb"
|
||||
|
||||
[dependencies.dec]
|
||||
version = "1.10.3"
|
||||
version = "1.16.0"
|
||||
package = "rust_decimal"
|
||||
features = ["maths", "serde-float"]
|
||||
|
||||
[dependencies.geo]
|
||||
version = "0.18.0"
|
||||
features = ["use-serde"]
|
||||
|
||||
[dependencies.fern]
|
||||
version = "0.6.0"
|
||||
|
@ -38,12 +54,16 @@ features = ["serde", "v4"]
|
|||
version = "0.3.1"
|
||||
features = ["compression", "websocket"]
|
||||
|
||||
[dependencies.tikv]
|
||||
version = "0.1.0"
|
||||
package = "tikv-client"
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.4.0"
|
||||
version = "1.12.0"
|
||||
features = ["macros"]
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.11.2"
|
||||
version = "0.11.5"
|
||||
features = ["blocking"]
|
||||
|
||||
[dependencies.chrono]
|
||||
|
@ -51,11 +71,11 @@ version = "0.4.19"
|
|||
features = ["serde"]
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.125"
|
||||
version = "1.0.130"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.serde_cbor]
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0.64"
|
||||
version = "1.0.68"
|
||||
|
|
2
src/cnf/mod.rs
Normal file
2
src/cnf/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Specifies how many subqueries will be processed recursively before the query fails.
|
||||
pub const MAX_RECURSIVE_QUERIES: usize = 16;
|
|
@ -17,3 +17,6 @@ pub use self::process::*;
|
|||
pub use self::response::*;
|
||||
pub use self::runtime::*;
|
||||
pub use self::session::*;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::dbs::executor::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
pub trait Process {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error>;
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -6,9 +6,10 @@ pub struct Responses(pub Vec<Response>);
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Response {
|
||||
pub sql: String,
|
||||
pub time: String,
|
||||
pub status: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub result: Option<Literal>,
|
||||
pub detail: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub result: Option<Value>,
|
||||
}
|
||||
|
|
11
src/dbs/test.rs
Normal file
11
src/dbs/test.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::executor::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
|
||||
pub fn mock<'a>() -> (Runtime, Options<'a>, Executor) {
|
||||
let ctx = Context::default().freeze();
|
||||
let opt = Options::default();
|
||||
let exe = Executor::new();
|
||||
(ctx, opt, exe)
|
||||
}
|
135
src/err/mod.rs
135
src/err/mod.rs
|
@ -1,11 +1,29 @@
|
|||
use crate::sql::duration::Duration;
|
||||
use crate::key::bytes::decode::Error as DecodeError;
|
||||
use crate::key::bytes::encode::Error as EncodeError;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use echodb::err::Error as EchoDBError;
|
||||
use http::Error as HttpError;
|
||||
use serde_cbor::error::Error as CborError;
|
||||
use serde_json::error::Error as JsonError;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tikv::Error as TiKVError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Couldn't setup connection to underlying datastore")]
|
||||
DsError,
|
||||
|
||||
#[error("Couldn't create a database transaction")]
|
||||
TxError,
|
||||
|
||||
#[error("Couldn't update a finished transaction")]
|
||||
TxFinishedError,
|
||||
|
||||
#[error("Couldn't write to a read only transaction")]
|
||||
TxReadonlyError,
|
||||
|
||||
#[error("Specify a namespace to use")]
|
||||
NsError,
|
||||
|
||||
|
@ -27,58 +45,105 @@ pub enum Error {
|
|||
sql: String,
|
||||
},
|
||||
|
||||
#[error("Wrong number of arguments at position {pos} when parsing '{sql}'")]
|
||||
CountError {
|
||||
pos: usize,
|
||||
sql: String,
|
||||
#[error("Problem with embedded script function. {message}")]
|
||||
LanguageError {
|
||||
message: String,
|
||||
},
|
||||
|
||||
#[error("Query timeout of {timer} exceeded")]
|
||||
TimerError {
|
||||
#[error("Incorrect arguments for function {name}(). {message}")]
|
||||
ArgumentsError {
|
||||
name: String,
|
||||
message: String,
|
||||
},
|
||||
|
||||
#[error("Query timeout of {timer:?} exceeded")]
|
||||
QueryTimeoutError {
|
||||
timer: Duration,
|
||||
},
|
||||
|
||||
#[error("You don't have permission to perform this query type")]
|
||||
QueryPermissionsError,
|
||||
|
||||
#[error("You don't have permission to change to the {ns} namespace")]
|
||||
NsAuthenticationError {
|
||||
ns: String,
|
||||
},
|
||||
|
||||
#[error("You don't have permission to change to the {db} database")]
|
||||
DbAuthenticationError {
|
||||
db: String,
|
||||
},
|
||||
|
||||
#[error("Too many recursive subqueries have been set")]
|
||||
RecursiveSubqueryError {
|
||||
limit: usize,
|
||||
},
|
||||
|
||||
#[error("Can not execute CREATE query using value '{value}'")]
|
||||
CreateStatementError {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("Can not execute UPDATE query using value '{value}'")]
|
||||
UpdateStatementError {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("Can not execute RELATE query using value '{value}'")]
|
||||
RelateStatementError {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("Can not execute DELETE query using value '{value}'")]
|
||||
DeleteStatementError {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("Can not execute INSERT query using value '{value}'")]
|
||||
InsertStatementError {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("You don't have permission to run the `{query}` query on the `{table}` table")]
|
||||
TablePermissionsError {
|
||||
query: String,
|
||||
table: String,
|
||||
},
|
||||
|
||||
#[error("Unable to write to the `{table}` table while setup as a view")]
|
||||
TableViewError {
|
||||
table: String,
|
||||
},
|
||||
|
||||
#[error("Database record `{thing}` already exists")]
|
||||
ExistError {
|
||||
RecordExistsError {
|
||||
thing: Thing,
|
||||
},
|
||||
|
||||
#[error("Database index `{index}` already contains `{thing}`")]
|
||||
IndexError {
|
||||
RecordIndexError {
|
||||
index: String,
|
||||
thing: Thing,
|
||||
},
|
||||
|
||||
#[error("You don't have permission to perform the query `{query}`")]
|
||||
PermsError {
|
||||
query: String,
|
||||
},
|
||||
#[error("Key encoding error: {0}")]
|
||||
EncodeError(#[from] EncodeError),
|
||||
|
||||
#[error("Unable to write to the `{table}` table while setup as a view")]
|
||||
WriteError {
|
||||
table: String,
|
||||
},
|
||||
#[error("Key decoding error: {0}")]
|
||||
DecodeError(#[from] DecodeError),
|
||||
|
||||
#[error("You don't have permission to perform this query on the `{table}` table")]
|
||||
TableError {
|
||||
table: String,
|
||||
},
|
||||
#[error("Datastore error: {0}")]
|
||||
EchoDBError(#[from] EchoDBError),
|
||||
|
||||
#[error("Datastore error: {0}")]
|
||||
TiKVError(#[from] TiKVError),
|
||||
|
||||
#[error("HTTP Error: {0}")]
|
||||
HttpError(#[from] HttpError),
|
||||
|
||||
#[error("JSON Error: {0}")]
|
||||
JsonError(JsonError),
|
||||
JsonError(#[from] JsonError),
|
||||
|
||||
#[error("CBOR Error: {0}")]
|
||||
CborError(CborError),
|
||||
}
|
||||
|
||||
impl From<JsonError> for Error {
|
||||
fn from(err: JsonError) -> Error {
|
||||
Error::JsonError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CborError> for Error {
|
||||
fn from(err: CborError) -> Error {
|
||||
Error::CborError(err)
|
||||
}
|
||||
CborError(#[from] CborError),
|
||||
}
|
||||
|
|
23
src/mac/mod.rs
Normal file
23
src/mac/mod.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
macro_rules! map {
|
||||
($($k:expr => $v:expr),* $(,)?) => {{
|
||||
let mut m = ::std::collections::BTreeMap::new();
|
||||
$(m.insert($k, $v);)+
|
||||
m
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! hmap {
|
||||
($($k:expr => $v:expr),* $(,)?) => {{
|
||||
let mut m = ::std::collections::HashMap::new();
|
||||
$(m.insert($k, $v);)+
|
||||
m
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! bmap {
|
||||
($($k:expr => $v:expr),* $(,)?) => {{
|
||||
let mut m = ::std::collections::BTreeMap::new();
|
||||
$(m.insert($k, $v);)+
|
||||
m
|
||||
}};
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
#[macro_use]
|
||||
extern crate maplit;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
mod mac;
|
||||
|
||||
mod cli;
|
||||
mod cnf;
|
||||
mod ctx;
|
||||
mod dbs;
|
||||
mod doc;
|
||||
mod err;
|
||||
mod fnc;
|
||||
mod key;
|
||||
mod kvs;
|
||||
mod sql;
|
||||
mod web;
|
||||
|
|
262
src/sql/array.rs
262
src/sql/array.rs
|
@ -1,13 +1,13 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::expression::expression;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::strand::Strand;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::combinator::opt;
|
||||
use nom::multi::separated_list0;
|
||||
|
@ -15,14 +15,83 @@ use nom::IResult;
|
|||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
const NAME: &'static str = "Array";
|
||||
use std::ops;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
|
||||
pub struct Array {
|
||||
pub value: Vec<Value>,
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for Array {
|
||||
fn from(v: Vec<Value>) -> Self {
|
||||
Array {
|
||||
value: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<i32>> for Array {
|
||||
fn from(v: Vec<i32>) -> Self {
|
||||
Array {
|
||||
value: v.into_iter().map(|x| x.into()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<String>> for Array {
|
||||
fn from(v: Vec<String>) -> Self {
|
||||
Array {
|
||||
value: v.into_iter().map(|x| x.into()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Vec<Value>>> for Array {
|
||||
fn from(v: Vec<Vec<Value>>) -> Self {
|
||||
Array {
|
||||
value: v.into_iter().map(|x| x.into()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Vec<&str>> for Array {
|
||||
fn from(v: Vec<&str>) -> Self {
|
||||
Array {
|
||||
value: v.into_iter().map(|v| Value::from(v)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Array {
|
||||
pub fn len(&self) -> usize {
|
||||
self.value.len()
|
||||
}
|
||||
|
||||
pub fn as_ints(self) -> Vec<i64> {
|
||||
self.value.into_iter().map(|v| v.as_int()).collect()
|
||||
}
|
||||
|
||||
pub fn as_floats(self) -> Vec<f64> {
|
||||
self.value.into_iter().map(|v| v.as_float()).collect()
|
||||
}
|
||||
|
||||
pub fn as_numbers(self) -> Vec<Number> {
|
||||
self.value.into_iter().map(|v| v.as_number()).collect()
|
||||
}
|
||||
|
||||
pub fn as_strands(self) -> Vec<Strand> {
|
||||
self.value.into_iter().map(|v| v.as_strand()).collect()
|
||||
}
|
||||
|
||||
pub fn as_point(mut self) -> [f64; 2] {
|
||||
match self.len() {
|
||||
0 => [0.0, 0.0],
|
||||
1 => [self.value.remove(0).as_float(), 0.0],
|
||||
_ => [self.value.remove(0).as_float(), self.value.remove(0).as_float()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Array {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
|
@ -37,18 +106,19 @@ impl dbs::Process for Array {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
self.value
|
||||
.iter()
|
||||
.map(|v| match v.process(ctx, exe, doc) {
|
||||
Ok(v) => Ok(Value::from(v)),
|
||||
.map(|v| match v.process(ctx, opt, exe, doc) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(|v| {
|
||||
Literal::Array(Array {
|
||||
Value::Array(Array {
|
||||
value: v,
|
||||
})
|
||||
})
|
||||
|
@ -63,13 +133,175 @@ impl Serialize for Array {
|
|||
if serializer.is_human_readable() {
|
||||
serializer.serialize_some(&self.value)
|
||||
} else {
|
||||
let mut val = serializer.serialize_struct(NAME, 1)?;
|
||||
let mut val = serializer.serialize_struct("Array", 1)?;
|
||||
val.serialize_field("value", &self.value)?;
|
||||
val.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
impl ops::Add<Value> for Array {
|
||||
type Output = Self;
|
||||
fn add(mut self, other: Value) -> Self {
|
||||
if self.value.iter().position(|x| *x == other).is_none() {
|
||||
self.value.push(other)
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for Array {
|
||||
type Output = Self;
|
||||
fn add(mut self, other: Self) -> Self {
|
||||
for v in other.value {
|
||||
if self.value.iter().position(|x| *x == v).is_none() {
|
||||
self.value.push(v)
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
impl ops::Sub<Value> for Array {
|
||||
type Output = Self;
|
||||
fn sub(mut self, other: Value) -> Self {
|
||||
if let Some(p) = self.value.iter().position(|x| *x == other) {
|
||||
self.value.remove(p);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for Array {
|
||||
type Output = Self;
|
||||
fn sub(mut self, other: Self) -> Self {
|
||||
for v in other.value {
|
||||
if let Some(p) = self.value.iter().position(|x| *x == v) {
|
||||
self.value.remove(p);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Uniq<T> {
|
||||
fn uniq(self) -> Vec<T>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Uniq<T> for Vec<T> {
|
||||
fn uniq(mut self) -> Vec<T> {
|
||||
for x in (0..self.len()).rev() {
|
||||
for y in (x + 1..self.len()).rev() {
|
||||
if self[x] == self[y] {
|
||||
self.remove(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Union<T> {
|
||||
fn union(self, other: Vec<T>) -> Vec<T>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Union<T> for Vec<T> {
|
||||
fn union(mut self, mut other: Vec<T>) -> Vec<T> {
|
||||
self.append(&mut other);
|
||||
self.uniq()
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Combine<T> {
|
||||
fn combine(self, other: Vec<T>) -> Vec<Vec<T>>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Clone> Combine<T> for Vec<T> {
|
||||
fn combine(self, other: Vec<T>) -> Vec<Vec<T>> {
|
||||
let mut out = Vec::new();
|
||||
for a in self.iter() {
|
||||
for b in other.iter() {
|
||||
if a != b {
|
||||
out.push(vec![a.clone(), b.clone()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Concat<T> {
|
||||
fn concat(self, other: Vec<T>) -> Vec<Vec<T>>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Clone> Concat<T> for Vec<T> {
|
||||
fn concat(self, other: Vec<T>) -> Vec<Vec<T>> {
|
||||
let mut out = Vec::new();
|
||||
for a in self.iter() {
|
||||
for b in other.iter() {
|
||||
out.push(vec![a.clone(), b.clone()]);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Intersect<T> {
|
||||
fn intersect(self, other: Vec<T>) -> Vec<T>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Intersect<T> for Vec<T> {
|
||||
fn intersect(self, other: Vec<T>) -> Vec<T> {
|
||||
let mut out = Vec::new();
|
||||
let mut other: Vec<_> = other.into_iter().collect();
|
||||
for a in self.into_iter() {
|
||||
if let Some(pos) = other.iter().position(|b| a == *b) {
|
||||
out.push(a);
|
||||
other.remove(pos);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub trait Difference<T> {
|
||||
fn difference(self, other: Vec<T>) -> Vec<T>;
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Difference<T> for Vec<T> {
|
||||
fn difference(self, other: Vec<T>) -> Vec<T> {
|
||||
let mut out = Vec::new();
|
||||
let mut other: Vec<_> = other.into_iter().collect();
|
||||
for a in self.into_iter() {
|
||||
if let Some(pos) = other.iter().position(|b| a == *b) {
|
||||
other.remove(pos);
|
||||
} else {
|
||||
out.push(a);
|
||||
}
|
||||
}
|
||||
out.append(&mut other);
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
pub fn array(i: &str) -> IResult<&str, Array> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
|
@ -87,8 +319,8 @@ pub fn array(i: &str) -> IResult<&str, Array> {
|
|||
}
|
||||
|
||||
fn item(i: &str) -> IResult<&str, Value> {
|
||||
let (i, v) = expression(i)?;
|
||||
Ok((i, Value::from(v)))
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_while;
|
||||
use nom::bytes::complete::take_while_m_n;
|
||||
use nom::character::complete::multispace0;
|
||||
use nom::character::is_alphanumeric;
|
||||
use nom::combinator::map;
|
||||
use nom::error::ErrorKind;
|
||||
use nom::error::Error;
|
||||
use nom::error::ErrorKind;
|
||||
use nom::multi::many1;
|
||||
use nom::IResult;
|
||||
use std::ops::RangeBounds;
|
||||
use std::str;
|
||||
|
||||
pub fn colons(i: &str) -> IResult<&str, ()> {
|
||||
let (i, _) = multispace0(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = many1(tag(";"))(i)?;
|
||||
let (i, _) = multispace0(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, ()))
|
||||
}
|
||||
|
||||
pub fn commas(i: &str) -> IResult<&str, ()> {
|
||||
let (i, _) = multispace0(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(",")(i)?;
|
||||
let (i, _) = multispace0(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, ()))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Cond {
|
||||
pub expr: Expression,
|
||||
pub expr: Value,
|
||||
}
|
||||
|
||||
impl fmt::Display for Cond {
|
||||
|
@ -19,7 +19,7 @@ impl fmt::Display for Cond {
|
|||
pub fn cond(i: &str) -> IResult<&str, Cond> {
|
||||
let (i, _) = tag_no_case("WHERE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Cond {
|
||||
|
|
122
src/sql/data.rs
122
src/sql/data.rs
|
@ -2,10 +2,10 @@ use crate::sql::array::{array, Array};
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::object::{object, Object};
|
||||
use crate::sql::operator::{assigner, Operator};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::multi::separated_list1;
|
||||
|
@ -15,15 +15,27 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Data {
|
||||
SetExpression(Vec<(Idiom, Operator, Expression)>),
|
||||
EmptyExpression,
|
||||
SetExpression(Vec<(Idiom, Operator, Value)>),
|
||||
DiffExpression(Array),
|
||||
MergeExpression(Object),
|
||||
ContentExpression(Object),
|
||||
ReplaceExpression(Value),
|
||||
ContentExpression(Value),
|
||||
SingleExpression(Value),
|
||||
ValuesExpression(Vec<Vec<(Idiom, Value)>>),
|
||||
UpdateExpression(Vec<(Idiom, Operator, Value)>),
|
||||
}
|
||||
|
||||
impl Default for Data {
|
||||
fn default() -> Data {
|
||||
Data::EmptyExpression
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Data {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Data::EmptyExpression => write!(f, ""),
|
||||
Data::SetExpression(v) => write!(
|
||||
f,
|
||||
"SET {}",
|
||||
|
@ -34,13 +46,40 @@ impl fmt::Display for Data {
|
|||
),
|
||||
Data::DiffExpression(v) => write!(f, "DIFF {}", v),
|
||||
Data::MergeExpression(v) => write!(f, "MERGE {}", v),
|
||||
Data::ReplaceExpression(v) => write!(f, "REPLACE {}", v),
|
||||
Data::ContentExpression(v) => write!(f, "CONTENT {}", v),
|
||||
Data::SingleExpression(v) => write!(f, "{}", v),
|
||||
Data::ValuesExpression(v) => write!(
|
||||
f,
|
||||
"({}) VALUES {}",
|
||||
v.first()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| format!("{}", v.0))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
v.iter()
|
||||
.map(|v| format!(
|
||||
"({})",
|
||||
v.iter().map(|v| format!("{}", v.1)).collect::<Vec<_>>().join(", ")
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Data::UpdateExpression(v) => write!(
|
||||
f,
|
||||
"ON DUPLICATE KEY UPDATE {}",
|
||||
v.iter()
|
||||
.map(|(l, o, r)| format!("{} {} {}", l, o, r))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(i: &str) -> IResult<&str, Data> {
|
||||
alt((set, diff, merge, content))(i)
|
||||
alt((set, diff, merge, replace, content))(i)
|
||||
}
|
||||
|
||||
fn set(i: &str) -> IResult<&str, Data> {
|
||||
|
@ -51,7 +90,7 @@ fn set(i: &str) -> IResult<&str, Data> {
|
|||
let (i, _) = mightbespace(i)?;
|
||||
let (i, o) = assigner(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, r) = expression(i)?;
|
||||
let (i, r) = value(i)?;
|
||||
Ok((i, (l, o, r)))
|
||||
})(i)?;
|
||||
Ok((i, Data::SetExpression(v)))
|
||||
|
@ -71,13 +110,63 @@ fn merge(i: &str) -> IResult<&str, Data> {
|
|||
Ok((i, Data::MergeExpression(v)))
|
||||
}
|
||||
|
||||
fn replace(i: &str) -> IResult<&str, Data> {
|
||||
let (i, _) = tag_no_case("REPLACE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, Data::ReplaceExpression(v)))
|
||||
}
|
||||
|
||||
fn content(i: &str) -> IResult<&str, Data> {
|
||||
let (i, _) = tag_no_case("CONTENT")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = object(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, Data::ContentExpression(v)))
|
||||
}
|
||||
|
||||
pub fn single(i: &str) -> IResult<&str, Data> {
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, Data::SingleExpression(v)))
|
||||
}
|
||||
|
||||
pub fn values(i: &str) -> IResult<&str, Data> {
|
||||
let (i, _) = tag_no_case("(")(i)?;
|
||||
let (i, fields) = separated_list1(commas, idiom)(i)?;
|
||||
let (i, _) = tag_no_case(")")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("VALUES")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, values) = separated_list1(commas, |i| {
|
||||
let (i, _) = tag_no_case("(")(i)?;
|
||||
let (i, v) = separated_list1(commas, value)(i)?;
|
||||
let (i, _) = tag_no_case(")")(i)?;
|
||||
Ok((i, v))
|
||||
})(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Data::ValuesExpression(
|
||||
values
|
||||
.into_iter()
|
||||
.map(|row| fields.iter().cloned().zip(row.into_iter()).collect())
|
||||
.collect(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn update(i: &str) -> IResult<&str, Data> {
|
||||
let (i, _) = tag_no_case("ON DUPLICATE KEY UPDATE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, |i| {
|
||||
let (i, l) = idiom(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, o) = assigner(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, r) = value(i)?;
|
||||
Ok((i, (l, o, r)))
|
||||
})(i)?;
|
||||
Ok((i, Data::UpdateExpression(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -127,4 +216,25 @@ mod tests {
|
|||
let out = res.unwrap().1;
|
||||
assert_eq!("CONTENT { field: true }", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn values_statement() {
|
||||
let sql = "(one, two, three) VALUES ($param, true, [1, 2, 3]), ($param, false, [4, 5, 6])";
|
||||
let res = values(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
"(one, two, three) VALUES ($param, true, [1, 2, 3]), ($param, false, [4, 5, 6])",
|
||||
format!("{}", out)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_statement() {
|
||||
let sql = "ON DUPLICATE KEY UPDATE field = true, other.field = false";
|
||||
let res = update(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("ON DUPLICATE KEY UPDATE field = true, other.field = false", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,22 @@ impl Default for Datetime {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Datetime {
|
||||
fn from(v: i64) -> Self {
|
||||
Datetime {
|
||||
value: Utc.timestamp(v, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DateTime<Utc>> for Datetime {
|
||||
fn from(v: DateTime<Utc>) -> Self {
|
||||
Datetime {
|
||||
value: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Datetime {
|
||||
fn from(s: &str) -> Self {
|
||||
match datetime_raw(s) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crate::sql::datetime::Datetime;
|
||||
use chrono::DurationRound;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::is_a;
|
||||
use nom::bytes::complete::tag;
|
||||
|
@ -5,6 +7,7 @@ use nom::IResult;
|
|||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
use std::str::FromStr;
|
||||
use std::time;
|
||||
|
||||
|
@ -14,6 +17,15 @@ pub struct Duration {
|
|||
pub value: time::Duration,
|
||||
}
|
||||
|
||||
impl From<time::Duration> for Duration {
|
||||
fn from(t: time::Duration) -> Self {
|
||||
Duration {
|
||||
input: format!("{:?}", t),
|
||||
value: t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Duration {
|
||||
fn from(s: &str) -> Self {
|
||||
match duration(s) {
|
||||
|
@ -35,7 +47,7 @@ impl Serialize for Duration {
|
|||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_some(&self.value)
|
||||
serializer.serialize_some(&self.input)
|
||||
} else {
|
||||
let mut val = serializer.serialize_struct("Duration", 2)?;
|
||||
val.serialize_field("input", &self.input)?;
|
||||
|
@ -45,6 +57,53 @@ impl Serialize for Duration {
|
|||
}
|
||||
}
|
||||
|
||||
impl ops::Add for Duration {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Duration::from(self.value + other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for Duration {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Duration::from(self.value - other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<Datetime> for Duration {
|
||||
type Output = Datetime;
|
||||
fn add(self, other: Datetime) -> Datetime {
|
||||
match chrono::Duration::from_std(self.value) {
|
||||
Ok(d) => Datetime::from(other.value + d),
|
||||
Err(_) => Datetime::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub<Datetime> for Duration {
|
||||
type Output = Datetime;
|
||||
fn sub(self, other: Datetime) -> Datetime {
|
||||
match chrono::Duration::from_std(self.value) {
|
||||
Ok(d) => Datetime::from(other.value - d),
|
||||
Err(_) => Datetime::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Div<Datetime> for Duration {
|
||||
type Output = Datetime;
|
||||
fn div(self, other: Datetime) -> Datetime {
|
||||
match chrono::Duration::from_std(self.value) {
|
||||
Ok(d) => match other.value.duration_trunc(d) {
|
||||
Ok(v) => Datetime::from(v),
|
||||
Err(_) => Datetime::default(),
|
||||
},
|
||||
Err(_) => Datetime::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duration(i: &str) -> IResult<&str, Duration> {
|
||||
duration_raw(i)
|
||||
}
|
||||
|
|
|
@ -1,48 +1,36 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::fnc;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::literal::{literal, Literal};
|
||||
use crate::sql::operator::{operator, Operator};
|
||||
use nom::branch::alt;
|
||||
use crate::sql::value::{single, value, Value};
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Expression {
|
||||
Single(Box<Literal>),
|
||||
Binary(Box<Literal>, Operator, Box<Expression>),
|
||||
pub struct Expression {
|
||||
l: Value,
|
||||
o: Operator,
|
||||
r: Value,
|
||||
}
|
||||
|
||||
impl Default for Expression {
|
||||
fn default() -> Expression {
|
||||
Expression::Single(Box::new(Literal::Null))
|
||||
Expression {
|
||||
l: Value::Null,
|
||||
o: Operator::default(),
|
||||
r: Value::Null,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Expression {
|
||||
fn from(s: &str) -> Self {
|
||||
expression(s).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Literal> for Expression {
|
||||
fn from(v: Literal) -> Self {
|
||||
Expression::Single(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Expression {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Expression::Single(ref l) => write!(f, "{}", l),
|
||||
Expression::Binary(ref l, ref o, ref r) => write!(f, "{} {} {}", l, o, r),
|
||||
}
|
||||
write!(f, "{} {} {}", self.l, self.o, self.r)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,32 +38,30 @@ impl dbs::Process for Expression {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
match self {
|
||||
Expression::Single(ref l) => l.process(ctx, exe, doc),
|
||||
Expression::Binary(ref l, ref o, ref r) => {
|
||||
let l = l.process(ctx, exe, doc)?;
|
||||
match o {
|
||||
Operator::Or => match l.as_bool() {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
let l = self.l.process(ctx, opt, exe, doc)?;
|
||||
match self.o {
|
||||
Operator::Or => match l.is_truthy() {
|
||||
true => return Ok(l), // No need to continue
|
||||
_ => {} // Continue
|
||||
},
|
||||
Operator::And => match l.as_bool() {
|
||||
Operator::And => match l.is_truthy() {
|
||||
false => return Ok(l), // No need to continue
|
||||
_ => {} // Continue
|
||||
},
|
||||
_ => {} // Continue
|
||||
}
|
||||
let r = r.process(ctx, exe, doc)?;
|
||||
match o {
|
||||
let r = self.r.process(ctx, opt, exe, doc)?;
|
||||
match self.o {
|
||||
Operator::Or => fnc::operate::or(l, r),
|
||||
Operator::And => fnc::operate::and(l, r),
|
||||
Operator::Add => fnc::operate::add(&l, &r),
|
||||
Operator::Sub => fnc::operate::sub(&l, &r),
|
||||
Operator::Mul => fnc::operate::mul(&l, &r),
|
||||
Operator::Div => fnc::operate::div(&l, &r),
|
||||
Operator::Add => fnc::operate::add(l, r),
|
||||
Operator::Sub => fnc::operate::sub(l, r),
|
||||
Operator::Mul => fnc::operate::mul(l, r),
|
||||
Operator::Div => fnc::operate::div(l, r),
|
||||
Operator::Equal => fnc::operate::equal(&l, &r),
|
||||
Operator::Exact => fnc::operate::exact(&l, &r),
|
||||
Operator::NotEqual => fnc::operate::not_equal(&l, &r),
|
||||
|
@ -92,37 +78,31 @@ impl dbs::Process for Expression {
|
|||
Operator::Contain => fnc::operate::contain(&l, &r),
|
||||
Operator::NotContain => fnc::operate::not_contain(&l, &r),
|
||||
Operator::ContainAll => fnc::operate::contain_all(&l, &r),
|
||||
Operator::ContainSome => fnc::operate::contain_some(&l, &r),
|
||||
Operator::ContainAny => fnc::operate::contain_any(&l, &r),
|
||||
Operator::ContainNone => fnc::operate::contain_none(&l, &r),
|
||||
Operator::Inside => fnc::operate::inside(&l, &r),
|
||||
Operator::NotInside => fnc::operate::not_inside(&l, &r),
|
||||
Operator::AllInside => fnc::operate::inside_all(&l, &r),
|
||||
Operator::SomeInside => fnc::operate::inside_some(&l, &r),
|
||||
Operator::AnyInside => fnc::operate::inside_any(&l, &r),
|
||||
Operator::NoneInside => fnc::operate::inside_none(&l, &r),
|
||||
Operator::Intersects => fnc::operate::intersects(&l, &r),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expression(i: &str) -> IResult<&str, Expression> {
|
||||
alt((binary, single))(i)
|
||||
}
|
||||
|
||||
pub fn binary(i: &str) -> IResult<&str, Expression> {
|
||||
let (i, l) = literal(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, l) = single(i)?;
|
||||
let (i, o) = operator(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, r) = expression(i)?;
|
||||
Ok((i, Expression::Binary(Box::new(l), o, Box::new(r))))
|
||||
}
|
||||
|
||||
pub fn single(i: &str) -> IResult<&str, Expression> {
|
||||
let (i, l) = literal(i)?;
|
||||
Ok((i, Expression::Single(Box::new(l))))
|
||||
let (i, r) = value(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Expression {
|
||||
l,
|
||||
o,
|
||||
r,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -131,16 +111,7 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn expression_single() {
|
||||
let sql = "true";
|
||||
let res = expression(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("true", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_double() {
|
||||
fn expression_statement() {
|
||||
let sql = "true AND false";
|
||||
let res = expression(sql);
|
||||
assert!(res.is_ok());
|
||||
|
@ -166,6 +137,24 @@ mod tests {
|
|||
assert_eq!("(3 * 3 * 3) = 27", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_right_opened() {
|
||||
let sql = "27 = 3 * 3 * 3";
|
||||
let res = expression(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("27 = 3 * 3 * 3", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_right_closed() {
|
||||
let sql = "27 = (3 * 3 * 3)";
|
||||
let res = expression(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("27 = (3 * 3 * 3)", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expression_both_opened() {
|
||||
let sql = "3 * 3 * 3 = 3 * 3 * 3";
|
||||
|
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Fetchs(Vec<Fetch>);
|
||||
pub struct Fetchs(pub Vec<Fetch>);
|
||||
|
||||
impl fmt::Display for Fetchs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -52,6 +52,7 @@ fn fetch_raw(i: &str) -> IResult<&str, Fetch> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn fetch_statement() {
|
||||
|
@ -62,7 +63,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Fetchs(vec![Fetch {
|
||||
fetch: Idiom::from("field")
|
||||
fetch: Idiom::parse("field")
|
||||
}])
|
||||
);
|
||||
assert_eq!("FETCH field", format!("{}", out));
|
||||
|
@ -78,10 +79,10 @@ mod tests {
|
|||
out,
|
||||
Fetchs(vec![
|
||||
Fetch {
|
||||
fetch: Idiom::from("field")
|
||||
fetch: Idiom::parse("field")
|
||||
},
|
||||
Fetch {
|
||||
fetch: Idiom::from("other.field")
|
||||
fetch: Idiom::parse("other.field")
|
||||
},
|
||||
])
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::multi::separated_list1;
|
||||
|
@ -9,8 +9,22 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Fields(Vec<Field>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Fields(pub Vec<Field>);
|
||||
|
||||
impl Fields {
|
||||
pub fn single(&self) -> Option<Idiom> {
|
||||
match self.0.len() {
|
||||
1 => match self.0.first() {
|
||||
Some(Field::All) => None,
|
||||
Some(Field::Alone(e)) => Some(e.to_idiom()),
|
||||
Some(Field::Alias(_, i)) => Some(i.to_owned()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Fields {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -23,11 +37,11 @@ pub fn fields(i: &str) -> IResult<&str, Fields> {
|
|||
Ok((i, Fields(v)))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Field {
|
||||
All,
|
||||
Alone(Expression),
|
||||
Alias(Expression, Idiom),
|
||||
Alone(Value),
|
||||
Alias(Value, Idiom),
|
||||
}
|
||||
|
||||
impl Default for Field {
|
||||
|
@ -40,8 +54,8 @@ impl fmt::Display for Field {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Field::All => write!(f, "*"),
|
||||
Field::Alone(ref e) => write!(f, "{}", e),
|
||||
Field::Alias(ref e, ref a) => write!(f, "{} AS {}", e, a),
|
||||
Field::Alone(e) => write!(f, "{}", e),
|
||||
Field::Alias(e, a) => write!(f, "{} AS {}", e, a),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,18 +64,18 @@ pub fn field(i: &str) -> IResult<&str, Field> {
|
|||
alt((all, alias, alone))(i)
|
||||
}
|
||||
|
||||
fn all(i: &str) -> IResult<&str, Field> {
|
||||
pub fn all(i: &str) -> IResult<&str, Field> {
|
||||
let (i, _) = tag_no_case("*")(i)?;
|
||||
Ok((i, Field::All))
|
||||
}
|
||||
|
||||
fn alone(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = expression(i)?;
|
||||
pub fn alone(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = value(i)?;
|
||||
Ok((i, Field::Alone(f)))
|
||||
}
|
||||
|
||||
fn alias(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = expression(i)?;
|
||||
pub fn alias(i: &str) -> IResult<&str, Field> {
|
||||
let (i, f) = value(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("AS")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::number::{number, Number};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Filter {
|
||||
All,
|
||||
Last,
|
||||
Number(Number),
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
impl From<Number> for Filter {
|
||||
fn from(v: Number) -> Self {
|
||||
Filter::Number(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for Filter {
|
||||
fn from(v: Expression) -> Self {
|
||||
Filter::Expression(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Filter {
|
||||
fn from(s: &str) -> Self {
|
||||
filter(s).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Filter::All => write!(f, "*"),
|
||||
Filter::Last => write!(f, "$"),
|
||||
Filter::Number(v) => write!(f, "{}", v),
|
||||
Filter::Expression(v) => write!(f, "WHERE {}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter(i: &str) -> IResult<&str, Filter> {
|
||||
alt((filter_all, filter_last, filter_number, filter_expression))(i)
|
||||
}
|
||||
|
||||
fn filter_all(i: &str) -> IResult<&str, Filter> {
|
||||
let (i, _) = tag("*")(i)?;
|
||||
Ok((i, Filter::All))
|
||||
}
|
||||
|
||||
fn filter_last(i: &str) -> IResult<&str, Filter> {
|
||||
let (i, _) = tag("$")(i)?;
|
||||
Ok((i, Filter::Last))
|
||||
}
|
||||
|
||||
fn filter_number(i: &str) -> IResult<&str, Filter> {
|
||||
let (i, v) = number(i)?;
|
||||
Ok((i, Filter::Number(v)))
|
||||
}
|
||||
|
||||
fn filter_expression(i: &str) -> IResult<&str, Filter> {
|
||||
let (i, _) = alt((tag_no_case("WHERE"), tag("?")))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
Ok((i, Filter::Expression(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn filter_all() {
|
||||
let sql = "*";
|
||||
let res = filter(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("*", format!("{}", out));
|
||||
assert_eq!(out, Filter::All);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_last() {
|
||||
let sql = "$";
|
||||
let res = filter(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$", format!("{}", out));
|
||||
assert_eq!(out, Filter::Last);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_number() {
|
||||
let sql = "0";
|
||||
let res = filter(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("0", format!("{}", out));
|
||||
assert_eq!(out, Filter::Number(Number::from("0")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_expression_question() {
|
||||
let sql = "? test = true";
|
||||
let res = filter(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("WHERE test = true", format!("{}", out));
|
||||
assert_eq!(out, Filter::Expression(Expression::from("test = true")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_expression_condition() {
|
||||
let sql = "WHERE test = true";
|
||||
let res = filter(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("WHERE test = true", format!("{}", out));
|
||||
assert_eq!(out, Filter::Expression(Expression::from("test = true")));
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::fnc;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::expression::{expression, single, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::script::{script, Script};
|
||||
use crate::sql::value::{single, value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::multi::separated_list0;
|
||||
|
@ -18,9 +18,10 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Function {
|
||||
Future(Expression),
|
||||
Cast(String, Expression),
|
||||
Normal(String, Vec<Expression>),
|
||||
Future(Value),
|
||||
Script(Script),
|
||||
Cast(String, Value),
|
||||
Normal(String, Vec<Value>),
|
||||
}
|
||||
|
||||
impl PartialOrd for Function {
|
||||
|
@ -33,7 +34,8 @@ impl PartialOrd for Function {
|
|||
impl fmt::Display for Function {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Function::Future(ref e) => write!(f, "fn() -> {{ {} }}", e),
|
||||
Function::Future(ref e) => write!(f, "fn::future -> {{ {} }}", e),
|
||||
Function::Script(ref s) => write!(f, "fn::script -> {{ {} }}", s),
|
||||
Function::Cast(ref s, ref e) => write!(f, "<{}> {}", s, e),
|
||||
Function::Normal(ref s, ref e) => write!(
|
||||
f,
|
||||
|
@ -49,22 +51,30 @@ impl dbs::Process for Function {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
Function::Future(ref e) => {
|
||||
let a = e.process(ctx, exe, doc)?;
|
||||
Function::Future(ref e) => match opt.futures {
|
||||
true => {
|
||||
let a = e.process(ctx, opt, exe, doc)?;
|
||||
fnc::future::run(ctx, a)
|
||||
}
|
||||
false => Ok(self.to_owned().into()),
|
||||
},
|
||||
Function::Script(ref s) => {
|
||||
let a = s.to_owned();
|
||||
fnc::script::run(ctx, a)
|
||||
}
|
||||
Function::Cast(ref s, ref e) => {
|
||||
let a = e.process(ctx, exe, doc)?;
|
||||
let a = e.process(ctx, opt, exe, doc)?;
|
||||
fnc::cast::run(ctx, s, a)
|
||||
}
|
||||
Function::Normal(ref s, ref e) => {
|
||||
let mut a: Vec<Literal> = vec![];
|
||||
let mut a: Vec<Value> = vec![];
|
||||
for v in e {
|
||||
let v = v.process(ctx, exe, doc)?;
|
||||
let v = v.process(ctx, opt, exe, doc)?;
|
||||
a.push(v);
|
||||
}
|
||||
fnc::run(ctx, s, a)
|
||||
|
@ -74,22 +84,35 @@ impl dbs::Process for Function {
|
|||
}
|
||||
|
||||
pub fn function(i: &str) -> IResult<&str, Function> {
|
||||
alt((casts, future, normal))(i)
|
||||
alt((casts, langs, future, normal))(i)
|
||||
}
|
||||
|
||||
fn future(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = tag("fn()")(i)?;
|
||||
let (i, _) = tag("fn::future")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("->")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, Function::Future(v)))
|
||||
}
|
||||
|
||||
fn langs(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = tag("fn::script")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("->")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = script(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, Function::Script(v)))
|
||||
}
|
||||
|
||||
fn casts(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = tag("<")(i)?;
|
||||
let (i, s) = function_casts(i)?;
|
||||
|
@ -103,7 +126,7 @@ fn normal(i: &str) -> IResult<&str, Function> {
|
|||
let (i, s) = function_names(i)?;
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list0(commas, expression)(i)?;
|
||||
let (i, v) = separated_list0(commas, value)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, Function::Normal(s.to_string(), v)))
|
||||
|
@ -126,8 +149,8 @@ fn function_names(i: &str) -> IResult<&str, &str> {
|
|||
alt((
|
||||
function_array,
|
||||
function_count,
|
||||
function_crypto,
|
||||
function_geo,
|
||||
function_hash,
|
||||
function_http,
|
||||
function_is,
|
||||
function_math,
|
||||
|
@ -141,21 +164,34 @@ fn function_names(i: &str) -> IResult<&str, &str> {
|
|||
|
||||
fn function_array(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("array::combine"),
|
||||
tag("array::concat"),
|
||||
tag("array::difference"),
|
||||
tag("array::distinct"),
|
||||
tag("array::intersect"),
|
||||
tag("array::len"),
|
||||
tag("array::union"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn function_count(i: &str) -> IResult<&str, &str> {
|
||||
tag("count")(i)
|
||||
}
|
||||
|
||||
fn function_crypto(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("count::all"),
|
||||
tag("count::if"),
|
||||
tag("count::not"),
|
||||
tag("count::oneof"),
|
||||
tag("count::between"),
|
||||
tag("count"),
|
||||
tag("crypto::md5"),
|
||||
tag("crypto::sha1"),
|
||||
tag("crypto::sha256"),
|
||||
tag("crypto::sha512"),
|
||||
tag("crypto::argon2::compare"),
|
||||
tag("crypto::argon2::generate"),
|
||||
tag("crypto::bcrypt::compare"),
|
||||
tag("crypto::bcrypt::generate"),
|
||||
tag("crypto::pbkdf2::compare"),
|
||||
tag("crypto::pbkdf2::generate"),
|
||||
tag("crypto::scrypt::compare"),
|
||||
tag("crypto::scrypt::generate"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -163,31 +199,13 @@ fn function_geo(i: &str) -> IResult<&str, &str> {
|
|||
alt((
|
||||
tag("geo::area"),
|
||||
tag("geo::bearing"),
|
||||
tag("geo::center"),
|
||||
tag("geo::centroid"),
|
||||
tag("geo::circle"),
|
||||
tag("geo::distance"),
|
||||
tag("geo::latitude"),
|
||||
tag("geo::longitude"),
|
||||
tag("geo::midpoint"),
|
||||
tag("geo::hash::decode"),
|
||||
tag("geo::hash::encode"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn function_hash(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("hash::md5"),
|
||||
tag("hash::sha1"),
|
||||
tag("hash::sha256"),
|
||||
tag("hash::sha512"),
|
||||
tag("hash::bcrypt::compare"),
|
||||
tag("hash::bcrypt::generate"),
|
||||
tag("hash::scrypt::compare"),
|
||||
tag("hash::scrypt::generate"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn function_http(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("http::head"),
|
||||
|
@ -196,12 +214,6 @@ fn function_http(i: &str) -> IResult<&str, &str> {
|
|||
tag("http::post"),
|
||||
tag("http::patch"),
|
||||
tag("http::delete"),
|
||||
tag("http::async::head"),
|
||||
tag("http::async::get"),
|
||||
tag("http::async::put"),
|
||||
tag("http::async::post"),
|
||||
tag("http::async::patch"),
|
||||
tag("http::async::delete"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
|
@ -227,13 +239,8 @@ fn function_math(i: &str) -> IResult<&str, &str> {
|
|||
tag("math::abs"),
|
||||
tag("math::bottom"),
|
||||
tag("math::ceil"),
|
||||
tag("math::correlation"),
|
||||
tag("math::count"),
|
||||
tag("math::covariance"),
|
||||
tag("math::fixed"),
|
||||
tag("math::floor"),
|
||||
tag("math::geometricmean"),
|
||||
tag("math::harmonicmean"),
|
||||
tag("math::interquartile"),
|
||||
)),
|
||||
alt((
|
||||
|
@ -247,8 +254,8 @@ fn function_math(i: &str) -> IResult<&str, &str> {
|
|||
alt((
|
||||
tag("math::nearestrank"),
|
||||
tag("math::percentile"),
|
||||
tag("math::product"),
|
||||
tag("math::round"),
|
||||
tag("math::sample"),
|
||||
tag("math::spread"),
|
||||
tag("math::sqrt"),
|
||||
tag("math::stddev"),
|
||||
|
@ -265,51 +272,24 @@ fn function_parse(i: &str) -> IResult<&str, &str> {
|
|||
tag("parse::email::domain"),
|
||||
tag("parse::email::user"),
|
||||
tag("parse::url::domain"),
|
||||
tag("parse::url::fragment"),
|
||||
tag("parse::url::host"),
|
||||
tag("parse::url::port"),
|
||||
tag("parse::url::path"),
|
||||
tag("parse::url::query"),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn function_rand(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
alt((
|
||||
tag("guid"),
|
||||
tag("uuid"),
|
||||
tag("rand::bool"),
|
||||
tag("rand::guid"),
|
||||
tag("rand::uuid"),
|
||||
tag("rand::enum"),
|
||||
tag("rand::time"),
|
||||
tag("rand::float"),
|
||||
tag("rand::guid"),
|
||||
tag("rand::int"),
|
||||
tag("rand::string"),
|
||||
tag("rand::integer"),
|
||||
tag("rand::decimal"),
|
||||
tag("rand::sentence"),
|
||||
tag("rand::paragraph"),
|
||||
)),
|
||||
alt((
|
||||
tag("rand::person::email"),
|
||||
tag("rand::person::phone"),
|
||||
tag("rand::person::fullname"),
|
||||
tag("rand::person::firstname"),
|
||||
tag("rand::person::lastname"),
|
||||
tag("rand::person::username"),
|
||||
tag("rand::person::jobtitle"),
|
||||
)),
|
||||
alt((
|
||||
tag("rand::location::name"),
|
||||
tag("rand::location::address"),
|
||||
tag("rand::location::street"),
|
||||
tag("rand::location::city"),
|
||||
tag("rand::location::state"),
|
||||
tag("rand::location::county"),
|
||||
tag("rand::location::zipcode"),
|
||||
tag("rand::location::postcode"),
|
||||
tag("rand::location::country"),
|
||||
tag("rand::location::altitude"),
|
||||
tag("rand::location::latitude"),
|
||||
tag("rand::location::longitude"),
|
||||
)),
|
||||
tag("rand::time"),
|
||||
tag("rand::uuid"),
|
||||
tag("rand"),
|
||||
))(i)
|
||||
}
|
||||
|
@ -319,15 +299,12 @@ fn function_string(i: &str) -> IResult<&str, &str> {
|
|||
tag("string::concat"),
|
||||
tag("string::contains"),
|
||||
tag("string::endsWith"),
|
||||
tag("string::format"),
|
||||
tag("string::includes"),
|
||||
tag("string::join"),
|
||||
tag("string::length"),
|
||||
tag("string::lowercase"),
|
||||
tag("string::repeat"),
|
||||
tag("string::replace"),
|
||||
tag("string::reverse"),
|
||||
tag("string::search"),
|
||||
tag("string::slice"),
|
||||
tag("string::slug"),
|
||||
tag("string::split"),
|
||||
|
@ -341,16 +318,16 @@ fn function_string(i: &str) -> IResult<&str, &str> {
|
|||
|
||||
fn function_time(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("time::now"),
|
||||
tag("time::add"),
|
||||
tag("time::age"),
|
||||
tag("time::floor"),
|
||||
tag("time::round"),
|
||||
tag("time::day"),
|
||||
tag("time::floor"),
|
||||
tag("time::hour"),
|
||||
tag("time::mins"),
|
||||
tag("time::month"),
|
||||
tag("time::nano"),
|
||||
tag("time::now"),
|
||||
tag("time::round"),
|
||||
tag("time::secs"),
|
||||
tag("time::unix"),
|
||||
tag("time::wday"),
|
||||
|
@ -362,11 +339,16 @@ fn function_time(i: &str) -> IResult<&str, &str> {
|
|||
|
||||
fn function_type(i: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
tag("type::batch"),
|
||||
tag("type::model"),
|
||||
tag("type::bool"),
|
||||
tag("type::datetime"),
|
||||
tag("type::decimal"),
|
||||
tag("type::duration"),
|
||||
tag("type::float"),
|
||||
tag("type::int"),
|
||||
tag("type::number"),
|
||||
tag("type::point"),
|
||||
tag("type::polygon"),
|
||||
tag("type::regex"),
|
||||
tag("type::string"),
|
||||
tag("type::table"),
|
||||
tag("type::thing"),
|
||||
))(i)
|
||||
|
@ -376,6 +358,8 @@ fn function_type(i: &str) -> IResult<&str, &str> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn function_single() {
|
||||
|
@ -389,12 +373,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn function_module() {
|
||||
let sql = "count::if()";
|
||||
let sql = "rand::uuid()";
|
||||
let res = function(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("count::if()", format!("{}", out));
|
||||
assert_eq!(out, Function::Normal(String::from("count::if"), vec![]));
|
||||
assert_eq!("rand::uuid()", format!("{}", out));
|
||||
assert_eq!(out, Function::Normal(String::from("rand::uuid"), vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -404,10 +388,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("is::numeric(NULL)", format!("{}", out));
|
||||
assert_eq!(
|
||||
out,
|
||||
Function::Normal(String::from("is::numeric"), vec![Expression::from("null")])
|
||||
);
|
||||
assert_eq!(out, Function::Normal(String::from("is::numeric"), vec![Value::Null]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -417,7 +398,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<int>1.2345", format!("{}", out));
|
||||
assert_eq!(out, Function::Cast(String::from("int"), Expression::from("1.2345")));
|
||||
assert_eq!(out, Function::Cast(String::from("int"), 1.2345.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -427,16 +408,34 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<string>1.2345", format!("{}", out));
|
||||
assert_eq!(out, Function::Cast(String::from("string"), Expression::from("1.2345")));
|
||||
assert_eq!(out, Function::Cast(String::from("string"), 1.2345.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_future_expression() {
|
||||
let sql = "fn() -> { 1.2345 + 5.4321 }";
|
||||
let sql = "fn::future -> { 1.2345 + 5.4321 }";
|
||||
let res = function(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("fn() -> { 1.2345 + 5.4321 }", format!("{}", out));
|
||||
assert_eq!(out, Function::Future(Expression::from("1.2345 + 5.4321")));
|
||||
assert_eq!("fn::future -> { 1.2345 + 5.4321 }", format!("{}", out));
|
||||
assert_eq!(out, Function::Future(Value::from(Expression::parse("1.2345 + 5.4321"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_script_expression() {
|
||||
let sql = "fn::script -> { 1.2345 + 5.4321 }";
|
||||
let res = function(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
"fn::script -> { return this.tags.filter(t => { return t.length > 3; }); }",
|
||||
format!("{}", out)
|
||||
);
|
||||
assert_eq!(
|
||||
out,
|
||||
Function::Script(Script::from(
|
||||
"return this.tags.filter(t => { return t.length > 3; });"
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
728
src/sql/geometry.rs
Normal file
728
src/sql/geometry.rs
Normal file
|
@ -0,0 +1,728 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use geo::algorithm::contains::Contains;
|
||||
use geo::algorithm::intersects::Intersects;
|
||||
use geo::{LineString, Point, Polygon};
|
||||
use geo::{MultiLineString, MultiPoint, MultiPolygon};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::multi::separated_list0;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::number::complete::double;
|
||||
use nom::sequence::delimited;
|
||||
use nom::sequence::preceded;
|
||||
use nom::IResult;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
const SINGLE: &str = r#"'"#;
|
||||
const DOUBLE: &str = r#"""#;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
pub enum Geometry {
|
||||
Point(Point<f64>),
|
||||
Line(LineString<f64>),
|
||||
Polygon(Polygon<f64>),
|
||||
MultiPoint(MultiPoint<f64>),
|
||||
MultiLine(MultiLineString<f64>),
|
||||
MultiPolygon(MultiPolygon<f64>),
|
||||
Collection(Vec<Geometry>),
|
||||
}
|
||||
|
||||
impl PartialOrd for Geometry {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f64, f64)> for Geometry {
|
||||
fn from(v: (f64, f64)) -> Self {
|
||||
Geometry::Point(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[f64; 2]> for Geometry {
|
||||
fn from(v: [f64; 2]) -> Self {
|
||||
Geometry::Point(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point<f64>> for Geometry {
|
||||
fn from(v: Point<f64>) -> Self {
|
||||
Geometry::Point(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LineString<f64>> for Geometry {
|
||||
fn from(v: LineString<f64>) -> Self {
|
||||
Geometry::Line(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Polygon<f64>> for Geometry {
|
||||
fn from(v: Polygon<f64>) -> Self {
|
||||
Geometry::Polygon(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiPoint<f64>> for Geometry {
|
||||
fn from(v: MultiPoint<f64>) -> Self {
|
||||
Geometry::MultiPoint(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiLineString<f64>> for Geometry {
|
||||
fn from(v: MultiLineString<f64>) -> Self {
|
||||
Geometry::MultiLine(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiPolygon<f64>> for Geometry {
|
||||
fn from(v: MultiPolygon<f64>) -> Self {
|
||||
Geometry::MultiPolygon(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Geometry>> for Geometry {
|
||||
fn from(v: Vec<Geometry>) -> Self {
|
||||
Geometry::Collection(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Point<f64>>> for Geometry {
|
||||
fn from(v: Vec<Point<f64>>) -> Self {
|
||||
Geometry::MultiPoint(MultiPoint(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<LineString<f64>>> for Geometry {
|
||||
fn from(v: Vec<LineString<f64>>) -> Self {
|
||||
Geometry::MultiLine(MultiLineString(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Polygon<f64>>> for Geometry {
|
||||
fn from(v: Vec<Polygon<f64>>) -> Self {
|
||||
Geometry::MultiPolygon(MultiPolygon(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Geometry> for geo::Geometry<f64> {
|
||||
fn from(v: Geometry) -> Self {
|
||||
match v {
|
||||
Geometry::Point(v) => v.into(),
|
||||
Geometry::Line(v) => v.into(),
|
||||
Geometry::Polygon(v) => v.into(),
|
||||
Geometry::MultiPoint(v) => v.into(),
|
||||
Geometry::MultiLine(v) => v.into(),
|
||||
Geometry::MultiPolygon(v) => v.into(),
|
||||
Geometry::Collection(v) => v.into_iter().collect::<geo::Geometry<f64>>().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Geometry> for geo::Geometry<f64> {
|
||||
fn from_iter<I: IntoIterator<Item = Geometry>>(iter: I) -> Self {
|
||||
let mut c: Vec<geo::Geometry<f64>> = vec![];
|
||||
for i in iter {
|
||||
c.push(i.into())
|
||||
}
|
||||
geo::Geometry::GeometryCollection(geo::GeometryCollection(c))
|
||||
}
|
||||
}
|
||||
|
||||
impl Geometry {
|
||||
// -----------------------------------
|
||||
// Value operations
|
||||
// -----------------------------------
|
||||
|
||||
pub fn contains(&self, other: &Geometry) -> bool {
|
||||
match self {
|
||||
Geometry::Point(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::MultiPoint(w) => w.iter().all(|x| v.contains(x)),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
_ => false,
|
||||
},
|
||||
Geometry::Line(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::Line(w) => v.contains(w),
|
||||
Geometry::MultiLine(w) => w.iter().all(|x| w.contains(x)),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
_ => false,
|
||||
},
|
||||
Geometry::Polygon(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::Line(w) => v.contains(w),
|
||||
Geometry::Polygon(w) => v.contains(w),
|
||||
Geometry::MultiPolygon(w) => w.iter().all(|x| w.contains(x)),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
_ => false,
|
||||
},
|
||||
Geometry::MultiPoint(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::MultiPoint(w) => w.iter().all(|x| w.contains(x)),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
_ => false,
|
||||
},
|
||||
Geometry::MultiLine(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::Line(w) => v.contains(w),
|
||||
Geometry::MultiLine(w) => w.iter().all(|x| w.contains(x)),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
_ => false,
|
||||
},
|
||||
Geometry::MultiPolygon(v) => match other {
|
||||
Geometry::Point(w) => v.contains(w),
|
||||
Geometry::Line(w) => v.contains(w),
|
||||
Geometry::Polygon(w) => v.contains(w),
|
||||
Geometry::MultiPoint(w) => v.contains(w),
|
||||
Geometry::MultiLine(w) => v.contains(w),
|
||||
Geometry::MultiPolygon(w) => v.contains(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.contains(x)),
|
||||
},
|
||||
Geometry::Collection(v) => v.iter().all(|x| x.contains(other)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: &Geometry) -> bool {
|
||||
match self {
|
||||
Geometry::Point(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::Line(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::Polygon(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => v.intersects(w),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::MultiPoint(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::MultiLine(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::MultiPolygon(v) => match other {
|
||||
Geometry::Point(w) => v.intersects(w),
|
||||
Geometry::Line(w) => v.intersects(w),
|
||||
Geometry::Polygon(w) => v.intersects(w),
|
||||
Geometry::MultiPoint(w) => v.intersects(w),
|
||||
Geometry::MultiLine(w) => v.intersects(w),
|
||||
Geometry::MultiPolygon(w) => v.intersects(w),
|
||||
Geometry::Collection(w) => w.iter().all(|x| self.intersects(x)),
|
||||
},
|
||||
Geometry::Collection(v) => v.iter().all(|x| x.intersects(other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Geometry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Geometry::Point(v) => write!(f, "({}, {})", v.x(), v.y()),
|
||||
_ => write!(f, "{}", serde_json::to_string(self).unwrap_or(String::from(""))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Geometry {
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if s.is_human_readable() {
|
||||
match self {
|
||||
Geometry::Point(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("Point")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(vec![v.x(), v.y()].as_slice())?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::Line(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("LineString")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(
|
||||
v.points_iter()
|
||||
.map(|p| vec![p.x(), p.y()])
|
||||
.collect::<Vec<Vec<f64>>>()
|
||||
.as_slice(),
|
||||
)?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::Polygon(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("Polygon")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(
|
||||
vec![v
|
||||
.exterior()
|
||||
.points_iter()
|
||||
.map(|p| vec![p.x(), p.y()])
|
||||
.collect::<Vec<Vec<f64>>>()]
|
||||
.into_iter()
|
||||
.chain(
|
||||
v.interiors()
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
i.points_iter()
|
||||
.map(|p| vec![p.x(), p.y()])
|
||||
.collect::<Vec<Vec<f64>>>()
|
||||
})
|
||||
.collect::<Vec<Vec<Vec<f64>>>>(),
|
||||
)
|
||||
.collect::<Vec<Vec<Vec<f64>>>>()
|
||||
.as_slice(),
|
||||
)?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::MultiPoint(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("MultiPoint")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(v.0.as_slice())?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::MultiLine(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("MultiLineString")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(v.0.as_slice())?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::MultiPolygon(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("MultiPolygon")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(v.0.as_slice())?;
|
||||
map.end()
|
||||
}
|
||||
Geometry::Collection(v) => {
|
||||
let mut map = s.serialize_map(Some(2))?;
|
||||
map.serialize_key("type")?;
|
||||
map.serialize_value("GeometryCollection")?;
|
||||
map.serialize_key("coordinates")?;
|
||||
map.serialize_value(v)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
Geometry::Point(v) => s.serialize_newtype_variant("Geometry", 0, "Point", v),
|
||||
Geometry::Line(v) => s.serialize_newtype_variant("Geometry", 1, "Line", v),
|
||||
Geometry::Polygon(v) => s.serialize_newtype_variant("Geometry", 2, "Polygon", v),
|
||||
Geometry::MultiPoint(v) => s.serialize_newtype_variant("Geometry", 3, "Points", v),
|
||||
Geometry::MultiLine(v) => s.serialize_newtype_variant("Geometry", 4, "Lines", v),
|
||||
Geometry::MultiPolygon(v) => {
|
||||
s.serialize_newtype_variant("Geometry", 5, "Polygons", v)
|
||||
}
|
||||
Geometry::Collection(v) => {
|
||||
s.serialize_newtype_variant("Geometry", 6, "Collection", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn geometry(i: &str) -> IResult<&str, Geometry> {
|
||||
alt((simple, point, line, polygon, multipoint, multiline, multipolygon, collection))(i)
|
||||
}
|
||||
|
||||
fn simple(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, x) = double(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(",")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, y) = double(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, Geometry::Point((x, y).into())))
|
||||
}
|
||||
|
||||
fn point(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, point_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, point_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, point_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, point_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn line(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, line_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, line_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, line_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, line_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn polygon(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, polygon_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, polygon_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, polygon_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, polygon_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn multipoint(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, multipoint_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, multipoint_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, multipoint_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, multipoint_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn multiline(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, multiline_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, multiline_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, multiline_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, multiline_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn multipolygon(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, multipolygon_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, multipolygon_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, multipolygon_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, multipolygon_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn collection(i: &str) -> IResult<&str, Geometry> {
|
||||
let (i, _) = tag("{")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
|i| {
|
||||
let (i, _) = preceded(key_type, collection_type)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, v) = preceded(key_vals, collection_vals)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
|i| {
|
||||
let (i, v) = preceded(key_vals, collection_vals)(i)?;
|
||||
let (i, _) = delimited(mightbespace, tag(","), mightbespace)(i)?;
|
||||
let (i, _) = preceded(key_type, collection_type)(i)?;
|
||||
Ok((i, v))
|
||||
},
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("}")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
fn point_vals(i: &str) -> IResult<&str, Point<f64>> {
|
||||
let (i, v) = coordinate(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn line_vals(i: &str) -> IResult<&str, LineString<f64>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, coordinate)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v.into()))
|
||||
}
|
||||
|
||||
fn polygon_vals(i: &str) -> IResult<&str, Polygon<f64>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, e) = line_vals(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
let (i, v) = separated_list0(commas, |i| {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = line_vals(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
})(i)?;
|
||||
Ok((i, Polygon::new(e, v)))
|
||||
}
|
||||
|
||||
fn multipoint_vals(i: &str) -> IResult<&str, Vec<Point<f64>>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, point_vals)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn multiline_vals(i: &str) -> IResult<&str, Vec<LineString<f64>>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, line_vals)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn multipolygon_vals(i: &str) -> IResult<&str, Vec<Polygon<f64>>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, polygon_vals)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn collection_vals(i: &str) -> IResult<&str, Vec<Geometry>> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, geometry)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
fn coordinate(i: &str) -> IResult<&str, (f64, f64)> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, x) = double(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(",")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, y) = double(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, (x, y)))
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
fn point_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("Point"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("Point"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn line_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("LineString"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("LineString"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn polygon_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("Polygon"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("Polygon"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn multipoint_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("MultiPoint"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("MultiPoint"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn multiline_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("MultiLineString"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("MultiLineString"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn multipolygon_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("MultiPolygon"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("MultiPolygon"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn collection_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
delimited(tag(SINGLE), tag("GeometryCollection"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("GeometryCollection"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
fn key_type(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
tag("type"),
|
||||
delimited(tag(SINGLE), tag("type"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("type"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(":")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn key_vals(i: &str) -> IResult<&str, &str> {
|
||||
let (i, v) = alt((
|
||||
tag("coordinates"),
|
||||
delimited(tag(SINGLE), tag("coordinates"), tag(SINGLE)),
|
||||
delimited(tag(DOUBLE), tag("coordinates"), tag(DOUBLE)),
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(":")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let sql = "(51.509865, -0.118092)";
|
||||
let res = geometry(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("(51.509865, -0.118092)", format!("{}", out));
|
||||
}
|
||||
}
|
204
src/sql/graph.rs
Normal file
204
src/sql/graph.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::table::{tables, Tables};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Dir {
|
||||
In,
|
||||
Out,
|
||||
Both,
|
||||
}
|
||||
|
||||
impl Default for Dir {
|
||||
fn default() -> Dir {
|
||||
Dir::Both
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Dir {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Dir::In => write!(f, "<-"),
|
||||
Dir::Out => write!(f, "->"),
|
||||
Dir::Both => write!(f, "<->"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Graph {
|
||||
pub dir: Dir,
|
||||
pub what: Tables,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cond: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub alias: Option<Idiom>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Graph {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.what.0.len() == 1 && self.cond.is_none() && self.alias.is_none() {
|
||||
write!(f, "{}{}", self.dir, self.what)
|
||||
} else {
|
||||
write!(f, "{}({}", self.dir, self.what)?;
|
||||
if let Some(ref v) = self.cond {
|
||||
write!(f, " WHERE {}", v)?
|
||||
}
|
||||
if let Some(ref v) = self.alias {
|
||||
write!(f, " AS {}", v)?
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn graph(i: &str) -> IResult<&str, Graph> {
|
||||
alt((graph_in, graph_out, graph_both))(i)
|
||||
}
|
||||
|
||||
fn graph_in(i: &str) -> IResult<&str, Graph> {
|
||||
let (i, _) = tag("<-")(i)?;
|
||||
let (i, (what, cond, alias)) = alt((simple, custom))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Graph {
|
||||
dir: Dir::In,
|
||||
what,
|
||||
cond,
|
||||
alias,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn graph_out(i: &str) -> IResult<&str, Graph> {
|
||||
let (i, _) = tag("->")(i)?;
|
||||
let (i, (what, cond, alias)) = alt((simple, custom))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Graph {
|
||||
dir: Dir::Out,
|
||||
what,
|
||||
cond,
|
||||
alias,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn graph_both(i: &str) -> IResult<&str, Graph> {
|
||||
let (i, _) = tag("<->")(i)?;
|
||||
let (i, (what, cond, alias)) = alt((simple, custom))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Graph {
|
||||
dir: Dir::Both,
|
||||
what,
|
||||
cond,
|
||||
alias,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn simple(i: &str) -> IResult<&str, (Tables, Option<Value>, Option<Idiom>)> {
|
||||
let (i, w) = what(i)?;
|
||||
Ok((i, (w, None, None)))
|
||||
}
|
||||
|
||||
fn custom(i: &str) -> IResult<&str, (Tables, Option<Value>, Option<Idiom>)> {
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, w) = what(i)?;
|
||||
let (i, c) = opt(cond)(i)?;
|
||||
let (i, a) = opt(alias)(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, (w, c, a)))
|
||||
}
|
||||
|
||||
fn what(i: &str) -> IResult<&str, Tables> {
|
||||
let (i, v) = tables(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn cond(i: &str) -> IResult<&str, Value> {
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("WHERE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
fn alias(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("AS")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = idiom(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn graph_in() {
|
||||
let sql = "<-likes";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<-likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_out() {
|
||||
let sql = "->likes";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("->likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_both() {
|
||||
let sql = "<->likes";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("<->likes", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_multiple() {
|
||||
let sql = "->(likes, follows)";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("->(likes, follows)", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_aliases() {
|
||||
let sql = "->(likes, follows AS connections)";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("->(likes, follows AS connections)", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_conditions() {
|
||||
let sql = "->(likes, follows WHERE influencer = true AS connections)";
|
||||
let res = graph(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("->(likes, follows WHERE influencer = true AS connections)", format!("{}", out));
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Groups(Vec<Group>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Groups(pub Vec<Group>);
|
||||
|
||||
impl fmt::Display for Groups {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -22,7 +22,7 @@ impl fmt::Display for Groups {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Group {
|
||||
pub group: Idiom,
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ fn group_raw(i: &str) -> IResult<&str, Group> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn group_statement() {
|
||||
|
@ -65,7 +66,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Groups(vec![Group {
|
||||
group: Idiom::from("field")
|
||||
group: Idiom::parse("field")
|
||||
}])
|
||||
);
|
||||
assert_eq!("GROUP BY field", format!("{}", out));
|
||||
|
@ -80,7 +81,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Groups(vec![Group {
|
||||
group: Idiom::from("field")
|
||||
group: Idiom::parse("field")
|
||||
}])
|
||||
);
|
||||
assert_eq!("GROUP BY field", format!("{}", out));
|
||||
|
@ -96,10 +97,10 @@ mod tests {
|
|||
out,
|
||||
Groups(vec![
|
||||
Group {
|
||||
group: Idiom::from("field")
|
||||
group: Idiom::parse("field")
|
||||
},
|
||||
Group {
|
||||
group: Idiom::from("other.field")
|
||||
group: Idiom::parse("other.field")
|
||||
},
|
||||
])
|
||||
);
|
||||
|
|
|
@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Ident {
|
||||
pub name: String,
|
||||
}
|
||||
|
|
145
src/sql/idiom.rs
145
src/sql/idiom.rs
|
@ -1,23 +1,21 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::common::{escape, val_char};
|
||||
use crate::sql::filter::{filter, Filter};
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::literal::Literal;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::combinator::opt;
|
||||
use crate::sql::part::{all, field, first, graph, index, part, Part};
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Idioms(Vec<Idiom>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Idioms(pub Vec<Idiom>);
|
||||
|
||||
impl fmt::Display for Idioms {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -25,92 +23,97 @@ impl fmt::Display for Idioms {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn idioms(i: &str) -> IResult<&str, Idioms> {
|
||||
let (i, v) = separated_list1(commas, idiom)(i)?;
|
||||
pub fn locals(i: &str) -> IResult<&str, Idioms> {
|
||||
let (i, v) = separated_list1(commas, local)(i)?;
|
||||
Ok((i, Idioms(v)))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Idiom {
|
||||
pub parts: Vec<(String, Option<Filter>)>,
|
||||
pub parts: Vec<Part>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Idiom {
|
||||
fn from(s: &str) -> Self {
|
||||
idiom(s).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(String, Option<Filter>)>> for Idiom {
|
||||
fn from(v: Vec<(String, Option<Filter>)>) -> Self {
|
||||
impl From<Vec<Part>> for Idiom {
|
||||
fn from(v: Vec<Part>) -> Self {
|
||||
Idiom {
|
||||
parts: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Idiom {
|
||||
pub fn next(&self) -> Idiom {
|
||||
match self.parts.len() {
|
||||
0 => Idiom::from(vec![]),
|
||||
_ => Idiom::from(self.parts[1..].to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Idiom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.parts.len() == 1 {
|
||||
match self.parts.first().unwrap() {
|
||||
(i, Some(ref a)) => write!(f, "{}[{}]", i, a),
|
||||
(i, None) => write!(f, "{}", escape(&i, &val_char, "`")),
|
||||
}
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.parts
|
||||
.iter()
|
||||
.map(|(ref i, ref a)| match a {
|
||||
Some(ref a) => format!("{}[{}]", i, a),
|
||||
None => format!("{}", escape(&i, &val_char, "`")),
|
||||
.enumerate()
|
||||
.map(|(i, p)| match (i, p) {
|
||||
(0, Part::Field(v)) => format!("{}", v),
|
||||
_ => format!("{}", p),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(".")
|
||||
.join("")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for Idiom {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match doc {
|
||||
// There is a current document
|
||||
Some(v) => v.get(ctx, opt, exe, self).ok(),
|
||||
// There isn't any document
|
||||
None => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, v) = separated_list1(tag("."), all)(i)?;
|
||||
// Used in a DEFINE FIELD and DEFINE INDEX clause
|
||||
pub fn local(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(alt((all, index, field)))(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
fn all(i: &str) -> IResult<&str, (String, Option<Filter>)> {
|
||||
let (i, v) = raw(i)?;
|
||||
let (i, a) = opt(fil)(i)?;
|
||||
Ok((i, (v, a)))
|
||||
// Used in a $param definition
|
||||
pub fn param(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
fn raw(i: &str) -> IResult<&str, String> {
|
||||
let (i, v) = ident_raw(i)?;
|
||||
Ok((i, String::from(v)))
|
||||
}
|
||||
|
||||
fn fil(i: &str) -> IResult<&str, Filter> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, v) = filter(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, v))
|
||||
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = alt((first, graph))(i)?;
|
||||
let (i, mut v) = many0(part)(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn idiom_normal() {
|
||||
|
@ -122,7 +125,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![(String::from("test"), None),],
|
||||
parts: vec![Part::from("test")],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -137,7 +140,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![(String::from("test"), None),],
|
||||
parts: vec![Part::from("test")],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -152,7 +155,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![(String::from("test"), None),],
|
||||
parts: vec![Part::from("test")],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -167,7 +170,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![(String::from("test"), None), (String::from("temp"), None),],
|
||||
parts: vec![Part::from("test"), Part::from("temp"),],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -182,7 +185,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![(String::from("test"), None), (String::from("some key"), None),],
|
||||
parts: vec![Part::from("test"), Part::from("some key"),],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -197,10 +200,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![
|
||||
(String::from("test"), None),
|
||||
(String::from("temp"), Some(Filter::from("*"))),
|
||||
],
|
||||
parts: vec![Part::from("test"), Part::from("temp"), Part::All,],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -215,10 +215,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![
|
||||
(String::from("test"), None),
|
||||
(String::from("temp"), Some(Filter::from("$"))),
|
||||
],
|
||||
parts: vec![Part::from("test"), Part::from("temp"), Part::Last,],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -233,11 +230,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Idiom {
|
||||
parts: vec![
|
||||
(String::from("test"), None),
|
||||
(String::from("temp"), Some(Filter::from("*"))),
|
||||
(String::from("text"), None),
|
||||
],
|
||||
parts: vec![Part::from("test"), Part::from("temp"), Part::All, Part::from("text")],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -253,9 +246,10 @@ mod tests {
|
|||
out,
|
||||
Idiom {
|
||||
parts: vec![
|
||||
(String::from("test"), None),
|
||||
(String::from("temp"), Some(Filter::from("WHERE test = true"))),
|
||||
(String::from("text"), None),
|
||||
Part::from("test"),
|
||||
Part::from("temp"),
|
||||
Part::from(Value::from(Expression::parse("test = true"))),
|
||||
Part::from("text")
|
||||
],
|
||||
}
|
||||
);
|
||||
|
@ -272,9 +266,10 @@ mod tests {
|
|||
out,
|
||||
Idiom {
|
||||
parts: vec![
|
||||
(String::from("test"), None),
|
||||
(String::from("temp"), Some(Filter::from("WHERE test = true"))),
|
||||
(String::from("text"), None),
|
||||
Part::from("test"),
|
||||
Part::from("temp"),
|
||||
Part::from(Value::from(Expression::parse("test = true"))),
|
||||
Part::from("text")
|
||||
],
|
||||
}
|
||||
);
|
||||
|
|
|
@ -12,13 +12,11 @@ pub enum Kind {
|
|||
Any,
|
||||
Array,
|
||||
Bool,
|
||||
Circle,
|
||||
Datetime,
|
||||
Number,
|
||||
Object,
|
||||
Point,
|
||||
Polygon,
|
||||
String,
|
||||
Geometry(String),
|
||||
Record(Vec<Table>),
|
||||
}
|
||||
|
||||
|
@ -34,17 +32,15 @@ impl fmt::Display for Kind {
|
|||
Kind::Any => write!(f, "any"),
|
||||
Kind::Array => write!(f, "array"),
|
||||
Kind::Bool => write!(f, "bool"),
|
||||
Kind::Circle => write!(f, "circle"),
|
||||
Kind::Datetime => write!(f, "datetime"),
|
||||
Kind::Number => write!(f, "number"),
|
||||
Kind::Object => write!(f, "object"),
|
||||
Kind::Point => write!(f, "point"),
|
||||
Kind::Polygon => write!(f, "polygon"),
|
||||
Kind::String => write!(f, "string"),
|
||||
Kind::Record(t) => write!(
|
||||
Kind::Geometry(v) => write!(f, "geometry({})", v),
|
||||
Kind::Record(v) => write!(
|
||||
f,
|
||||
"record({})",
|
||||
t.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
|
||||
v.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -52,19 +48,35 @@ impl fmt::Display for Kind {
|
|||
|
||||
pub fn kind(i: &str) -> IResult<&str, Kind> {
|
||||
alt((
|
||||
map(tag("any"), |_| Kind::Any),
|
||||
map(tag("array"), |_| Kind::Array),
|
||||
map(tag("bool"), |_| Kind::Bool),
|
||||
map(tag("circle"), |_| Kind::Circle),
|
||||
map(tag("datetime"), |_| Kind::Datetime),
|
||||
map(tag("number"), |_| Kind::Number),
|
||||
map(tag("object"), |_| Kind::Object),
|
||||
map(tag("point"), |_| Kind::Point),
|
||||
map(tag("polygon"), |_| Kind::Polygon),
|
||||
map(tag("string"), |_| Kind::String),
|
||||
map(geometry, |v| Kind::Geometry(v)),
|
||||
map(record, |v| Kind::Record(v)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
fn geometry(i: &str) -> IResult<&str, String> {
|
||||
let (i, _) = tag("geometry")(i)?;
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, v) = alt((
|
||||
tag("feature"),
|
||||
tag("point"),
|
||||
tag("line"),
|
||||
tag("polygon"),
|
||||
tag("multipoint"),
|
||||
tag("multiline"),
|
||||
tag("multipolygon"),
|
||||
tag("collection"),
|
||||
))(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, String::from(v)))
|
||||
}
|
||||
|
||||
fn record(i: &str) -> IResult<&str, Vec<Table>> {
|
||||
let (i, _) = tag("record")(i)?;
|
||||
let (i, _) = tag("(")(i)?;
|
||||
|
|
|
@ -7,14 +7,12 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Limit {
|
||||
pub expr: u64,
|
||||
}
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Limit(pub u64);
|
||||
|
||||
impl fmt::Display for Limit {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "LIMIT {}", self.expr)
|
||||
write!(f, "LIMIT {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,12 +21,7 @@ pub fn limit(i: &str) -> IResult<&str, Limit> {
|
|||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("BY"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = take_u64(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Limit {
|
||||
expr: v,
|
||||
},
|
||||
))
|
||||
Ok((i, Limit(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -42,12 +35,7 @@ mod tests {
|
|||
let res = limit(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
out,
|
||||
Limit {
|
||||
expr: 100
|
||||
}
|
||||
);
|
||||
assert_eq!(out, Limit(100));
|
||||
assert_eq!("LIMIT 100", format!("{}", out));
|
||||
}
|
||||
|
||||
|
@ -57,12 +45,7 @@ mod tests {
|
|||
let res = limit(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
out,
|
||||
Limit {
|
||||
expr: 100
|
||||
}
|
||||
);
|
||||
assert_eq!(out, Limit(100));
|
||||
assert_eq!("LIMIT 100", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,645 +0,0 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::fnc;
|
||||
use crate::sql::array::{array, Array};
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::datetime::{datetime, datetime_raw, Datetime};
|
||||
use crate::sql::duration::{duration, duration_raw, Duration};
|
||||
use crate::sql::function::{function, Function};
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::model::{model, Model};
|
||||
use crate::sql::number::{number, Number};
|
||||
use crate::sql::object::{object, Object};
|
||||
use crate::sql::param::{param, Param};
|
||||
use crate::sql::point::{point, Point};
|
||||
use crate::sql::polygon::{polygon, Polygon};
|
||||
use crate::sql::regex::{regex, Regex};
|
||||
use crate::sql::strand::{strand, Strand};
|
||||
use crate::sql::subquery::{subquery, Subquery};
|
||||
use crate::sql::table::{table, Table};
|
||||
use crate::sql::thing::{thing, Thing};
|
||||
use dec::prelude::ToPrimitive;
|
||||
use dec::Decimal;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::rest;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
const NAME: &'static str = "Literal";
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Literals(pub Vec<Literal>);
|
||||
|
||||
impl fmt::Display for Literals {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Literals {
|
||||
type Item = Literal;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn literals(i: &str) -> IResult<&str, Literals> {
|
||||
let (i, v) = separated_list1(commas, literal)(i)?;
|
||||
Ok((i, Literals(v)))
|
||||
}
|
||||
|
||||
pub fn whats(i: &str) -> IResult<&str, Literals> {
|
||||
let (i, v) = separated_list1(commas, what)(i)?;
|
||||
Ok((i, Literals(v)))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize)]
|
||||
pub enum Literal {
|
||||
None,
|
||||
Void,
|
||||
Null,
|
||||
False,
|
||||
True,
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Param(Param),
|
||||
Idiom(Idiom),
|
||||
Table(Table),
|
||||
Thing(Thing),
|
||||
Model(Model),
|
||||
Regex(Regex),
|
||||
Point(Point),
|
||||
Array(Array),
|
||||
Object(Object),
|
||||
Number(Number),
|
||||
Strand(Strand),
|
||||
Polygon(Polygon),
|
||||
Duration(Duration),
|
||||
Datetime(Datetime),
|
||||
Function(Function),
|
||||
Subquery(Subquery),
|
||||
}
|
||||
|
||||
impl Eq for Literal {}
|
||||
|
||||
impl Default for Literal {
|
||||
fn default() -> Literal {
|
||||
Literal::None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Literal {
|
||||
fn from(v: bool) -> Self {
|
||||
match v {
|
||||
true => Literal::True,
|
||||
false => Literal::False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for Literal {
|
||||
fn from(v: i8) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i16> for Literal {
|
||||
fn from(v: i16) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Literal {
|
||||
fn from(v: i32) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Literal {
|
||||
fn from(v: i64) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Literal {
|
||||
fn from(v: u8) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Literal {
|
||||
fn from(v: u16) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Literal {
|
||||
fn from(v: u32) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Literal {
|
||||
fn from(v: u64) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Literal {
|
||||
fn from(v: f32) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Literal {
|
||||
fn from(v: f64) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Number> for Literal {
|
||||
fn from(v: Number) -> Self {
|
||||
Literal::Number(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Decimal> for Literal {
|
||||
fn from(v: Decimal) -> Self {
|
||||
Literal::Number(Number::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Strand> for Literal {
|
||||
fn from(v: Strand) -> Self {
|
||||
Literal::Strand(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Literal {
|
||||
fn from(v: String) -> Self {
|
||||
Literal::Strand(Strand::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Literal {
|
||||
fn from(v: &str) -> Self {
|
||||
Literal::Strand(Strand::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn is_none(&self) -> bool {
|
||||
match self {
|
||||
Literal::None => true,
|
||||
Literal::Void => true,
|
||||
Literal::Null => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_void(&self) -> bool {
|
||||
match self {
|
||||
Literal::None => true,
|
||||
Literal::Void => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
match self {
|
||||
Literal::None => true,
|
||||
Literal::Null => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_true(&self) -> bool {
|
||||
match self {
|
||||
Literal::True => true,
|
||||
Literal::Strand(ref v) => v.value.to_ascii_lowercase() == "true",
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_false(&self) -> bool {
|
||||
match self {
|
||||
Literal::False => true,
|
||||
Literal::Strand(ref v) => v.value.to_ascii_lowercase() == "false",
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> bool {
|
||||
match self {
|
||||
Literal::True => true,
|
||||
Literal::False => false,
|
||||
Literal::Int(v) => v > &0,
|
||||
Literal::Float(v) => v > &0.0,
|
||||
Literal::Thing(_) => true,
|
||||
Literal::Point(_) => true,
|
||||
Literal::Polygon(_) => true,
|
||||
Literal::Array(ref v) => v.value.len() > 0,
|
||||
Literal::Object(ref v) => v.value.len() > 0,
|
||||
Literal::Strand(ref v) => v.value.to_ascii_lowercase() != "false",
|
||||
Literal::Number(ref v) => v.value > Decimal::new(0, 0),
|
||||
Literal::Duration(ref v) => v.value.as_nanos() > 0,
|
||||
Literal::Datetime(ref v) => v.value.timestamp() > 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_int(&self) -> i64 {
|
||||
match self {
|
||||
Literal::True => 1,
|
||||
Literal::Int(ref v) => v.clone(),
|
||||
Literal::Float(ref v) => *v as i64,
|
||||
Literal::Strand(ref v) => v.value.parse::<i64>().unwrap_or(0),
|
||||
Literal::Number(ref v) => v.value.to_i64().unwrap_or(0),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> f64 {
|
||||
match self {
|
||||
Literal::True => 1.0,
|
||||
Literal::Int(ref v) => *v as f64,
|
||||
Literal::Float(ref v) => v.clone(),
|
||||
Literal::Strand(ref v) => v.value.parse::<f64>().unwrap_or(0.0),
|
||||
Literal::Number(ref v) => v.value.to_f64().unwrap_or(0.0),
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_strand(&self) -> Strand {
|
||||
match self {
|
||||
Literal::Strand(ref v) => v.clone(),
|
||||
_ => Strand::from(self.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_number(&self) -> Number {
|
||||
match self {
|
||||
Literal::True => Number::from(1),
|
||||
Literal::Int(ref v) => Number::from(*v),
|
||||
Literal::Float(ref v) => Number::from(*v),
|
||||
Literal::Number(ref v) => v.clone(),
|
||||
Literal::Strand(ref v) => Number::from(v.value.as_str()),
|
||||
Literal::Duration(ref v) => v.value.as_secs().into(),
|
||||
Literal::Datetime(ref v) => v.value.timestamp().into(),
|
||||
_ => Number::from(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_datetime(&self) -> Datetime {
|
||||
match self {
|
||||
Literal::Strand(ref v) => Datetime::from(v.value.as_str()),
|
||||
Literal::Datetime(ref v) => v.clone(),
|
||||
_ => Datetime::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_duration(&self) -> Duration {
|
||||
match self {
|
||||
Literal::Strand(ref v) => Duration::from(v.value.as_str()),
|
||||
Literal::Duration(ref v) => v.clone(),
|
||||
_ => Duration::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Literal::None => write!(f, "NONE"),
|
||||
Literal::Void => write!(f, "VOID"),
|
||||
Literal::Null => write!(f, "NULL"),
|
||||
Literal::True => write!(f, "true"),
|
||||
Literal::False => write!(f, "false"),
|
||||
Literal::Int(v) => write!(f, "{}", v),
|
||||
Literal::Float(v) => write!(f, "{}", v),
|
||||
Literal::Param(ref v) => write!(f, "{}", v),
|
||||
Literal::Idiom(ref v) => write!(f, "{}", v),
|
||||
Literal::Table(ref v) => write!(f, "{}", v),
|
||||
Literal::Thing(ref v) => write!(f, "{}", v),
|
||||
Literal::Model(ref v) => write!(f, "{}", v),
|
||||
Literal::Regex(ref v) => write!(f, "{}", v),
|
||||
Literal::Point(ref v) => write!(f, "{}", v),
|
||||
Literal::Array(ref v) => write!(f, "{}", v),
|
||||
Literal::Object(ref v) => write!(f, "{}", v),
|
||||
Literal::Number(ref v) => write!(f, "{}", v),
|
||||
Literal::Strand(ref v) => write!(f, "{}", v),
|
||||
Literal::Polygon(ref v) => write!(f, "{}", v),
|
||||
Literal::Duration(ref v) => write!(f, "{}", v),
|
||||
Literal::Datetime(ref v) => write!(f, "{}", v),
|
||||
Literal::Function(ref v) => write!(f, "{}", v),
|
||||
Literal::Subquery(ref v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for Literal {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
match self {
|
||||
Literal::None => Ok(Literal::None),
|
||||
Literal::Void => Ok(Literal::Void),
|
||||
Literal::Null => Ok(Literal::Null),
|
||||
Literal::True => Ok(Literal::True),
|
||||
Literal::False => Ok(Literal::False),
|
||||
Literal::Param(ref v) => v.process(ctx, exe, doc),
|
||||
Literal::Idiom(ref v) => v.process(ctx, exe, doc),
|
||||
Literal::Array(ref v) => v.process(ctx, exe, doc),
|
||||
Literal::Object(ref v) => v.process(ctx, exe, doc),
|
||||
Literal::Function(ref v) => v.process(ctx, exe, doc),
|
||||
Literal::Subquery(ref v) => v.process(ctx, exe, doc),
|
||||
_ => Ok(self.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Literal {
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if s.is_human_readable() {
|
||||
match self {
|
||||
Literal::None => s.serialize_none(),
|
||||
Literal::Void => s.serialize_none(),
|
||||
Literal::Null => s.serialize_none(),
|
||||
Literal::True => s.serialize_bool(true),
|
||||
Literal::False => s.serialize_bool(false),
|
||||
Literal::Int(v) => s.serialize_i64(*v),
|
||||
Literal::Float(v) => s.serialize_f64(*v),
|
||||
Literal::Thing(ref v) => s.serialize_some(v),
|
||||
Literal::Point(ref v) => s.serialize_some(v),
|
||||
Literal::Array(ref v) => s.serialize_some(v),
|
||||
Literal::Object(ref v) => s.serialize_some(v),
|
||||
Literal::Number(ref v) => s.serialize_some(v),
|
||||
Literal::Strand(ref v) => s.serialize_some(v),
|
||||
Literal::Polygon(ref v) => s.serialize_some(v),
|
||||
Literal::Duration(ref v) => s.serialize_some(v),
|
||||
Literal::Datetime(ref v) => s.serialize_some(v),
|
||||
_ => s.serialize_none(),
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
Literal::None => s.serialize_unit_variant(NAME, 0, "None"),
|
||||
Literal::Void => s.serialize_unit_variant(NAME, 2, "Void"),
|
||||
Literal::Null => s.serialize_unit_variant(NAME, 1, "Null"),
|
||||
Literal::True => s.serialize_unit_variant(NAME, 3, "True"),
|
||||
Literal::False => s.serialize_unit_variant(NAME, 4, "False"),
|
||||
Literal::Int(ref v) => s.serialize_newtype_variant(NAME, 5, "Int", v),
|
||||
Literal::Float(ref v) => s.serialize_newtype_variant(NAME, 6, "Float", v),
|
||||
Literal::Param(ref v) => s.serialize_newtype_variant(NAME, 7, "Param", v),
|
||||
Literal::Idiom(ref v) => s.serialize_newtype_variant(NAME, 8, "Idiom", v),
|
||||
Literal::Table(ref v) => s.serialize_newtype_variant(NAME, 9, "Table", v),
|
||||
Literal::Thing(ref v) => s.serialize_newtype_variant(NAME, 10, "Thing", v),
|
||||
Literal::Model(ref v) => s.serialize_newtype_variant(NAME, 11, "Model", v),
|
||||
Literal::Regex(ref v) => s.serialize_newtype_variant(NAME, 12, "Regex", v),
|
||||
Literal::Point(ref v) => s.serialize_newtype_variant(NAME, 13, "Point", v),
|
||||
Literal::Array(ref v) => s.serialize_newtype_variant(NAME, 14, "Array", v),
|
||||
Literal::Object(ref v) => s.serialize_newtype_variant(NAME, 15, "Object", v),
|
||||
Literal::Number(ref v) => s.serialize_newtype_variant(NAME, 16, "Number", v),
|
||||
Literal::Strand(ref v) => s.serialize_newtype_variant(NAME, 17, "Strand", v),
|
||||
Literal::Polygon(ref v) => s.serialize_newtype_variant(NAME, 18, "Polygon", v),
|
||||
Literal::Duration(ref v) => s.serialize_newtype_variant(NAME, 19, "Duration", v),
|
||||
Literal::Datetime(ref v) => s.serialize_newtype_variant(NAME, 20, "Datetime", v),
|
||||
Literal::Function(ref v) => s.serialize_newtype_variant(NAME, 21, "Function", v),
|
||||
Literal::Subquery(ref v) => s.serialize_newtype_variant(NAME, 22, "Subquery", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for Literal {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
fnc::operate::add(&self, &other).unwrap_or(Literal::Null)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for Literal {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
fnc::operate::sub(&self, &other).unwrap_or(Literal::Null)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for Literal {
|
||||
type Output = Self;
|
||||
fn mul(self, other: Self) -> Self {
|
||||
fnc::operate::mul(&self, &other).unwrap_or(Literal::Null)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Div for Literal {
|
||||
type Output = Self;
|
||||
fn div(self, other: Self) -> Self {
|
||||
fnc::operate::div(&self, &other).unwrap_or(Literal::Null)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn literal(i: &str) -> IResult<&str, Literal> {
|
||||
alt((
|
||||
map(tag_no_case("NONE"), |_| Literal::None),
|
||||
map(tag_no_case("VOID"), |_| Literal::Void),
|
||||
map(tag_no_case("NULL"), |_| Literal::Null),
|
||||
map(tag_no_case("true"), |_| Literal::True),
|
||||
map(tag_no_case("false"), |_| Literal::False),
|
||||
map(subquery, |v| Literal::Subquery(v)),
|
||||
map(function, |v| Literal::Function(v)),
|
||||
map(datetime, |v| Literal::Datetime(v)),
|
||||
map(duration, |v| Literal::Duration(v)),
|
||||
map(polygon, |v| Literal::Polygon(v)),
|
||||
map(number, |v| Literal::Number(v)),
|
||||
map(strand, |v| Literal::Strand(v)),
|
||||
map(object, |v| Literal::Object(v)),
|
||||
map(array, |v| Literal::Array(v)),
|
||||
map(point, |v| Literal::Point(v)),
|
||||
map(param, |v| Literal::Param(v)),
|
||||
map(regex, |v| Literal::Regex(v)),
|
||||
map(thing, |v| Literal::Thing(v)),
|
||||
map(model, |v| Literal::Model(v)),
|
||||
map(idiom, |v| Literal::Idiom(v)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn what(i: &str) -> IResult<&str, Literal> {
|
||||
alt((
|
||||
map(param, |v| Literal::Param(v)),
|
||||
map(model, |v| Literal::Model(v)),
|
||||
map(regex, |v| Literal::Regex(v)),
|
||||
map(thing, |v| Literal::Thing(v)),
|
||||
map(table, |v| Literal::Table(v)),
|
||||
))(i)
|
||||
}
|
||||
|
||||
pub fn json(i: &str) -> IResult<&str, Literal> {
|
||||
alt((
|
||||
map(tag_no_case("NULL"), |_| Literal::Null),
|
||||
map(tag_no_case("true"), |_| Literal::True),
|
||||
map(tag_no_case("false"), |_| Literal::False),
|
||||
map(datetime_raw, |v| Literal::Datetime(v)),
|
||||
map(duration_raw, |v| Literal::Duration(v)),
|
||||
map(number, |v| Literal::Number(v)),
|
||||
map(object, |v| Literal::Object(v)),
|
||||
map(array, |v| Literal::Array(v)),
|
||||
map(rest, |v| Literal::Strand(Strand::from(v))),
|
||||
))(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn check_none() {
|
||||
assert_eq!(true, Literal::None.is_none());
|
||||
assert_eq!(true, Literal::Void.is_none());
|
||||
assert_eq!(true, Literal::Null.is_none());
|
||||
assert_eq!(false, Literal::from(1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_void() {
|
||||
assert_eq!(true, Literal::None.is_void());
|
||||
assert_eq!(true, Literal::Void.is_void());
|
||||
assert_eq!(false, Literal::Null.is_void());
|
||||
assert_eq!(false, Literal::from(1).is_void());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_null() {
|
||||
assert_eq!(true, Literal::None.is_null());
|
||||
assert_eq!(false, Literal::Void.is_null());
|
||||
assert_eq!(true, Literal::Null.is_null());
|
||||
assert_eq!(false, Literal::from(1).is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_true() {
|
||||
assert_eq!(false, Literal::None.is_true());
|
||||
assert_eq!(true, Literal::True.is_true());
|
||||
assert_eq!(false, Literal::False.is_true());
|
||||
assert_eq!(false, Literal::from(1).is_true());
|
||||
assert_eq!(true, Literal::from("true").is_true());
|
||||
assert_eq!(false, Literal::from("false").is_true());
|
||||
assert_eq!(false, Literal::from("something").is_true());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_false() {
|
||||
assert_eq!(false, Literal::None.is_false());
|
||||
assert_eq!(false, Literal::True.is_false());
|
||||
assert_eq!(true, Literal::False.is_false());
|
||||
assert_eq!(false, Literal::from(1).is_false());
|
||||
assert_eq!(false, Literal::from("true").is_false());
|
||||
assert_eq!(true, Literal::from("false").is_false());
|
||||
assert_eq!(false, Literal::from("something").is_false());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_bool() {
|
||||
assert_eq!(false, Literal::None.as_bool());
|
||||
assert_eq!(false, Literal::Null.as_bool());
|
||||
assert_eq!(false, Literal::Void.as_bool());
|
||||
assert_eq!(true, Literal::True.as_bool());
|
||||
assert_eq!(false, Literal::False.as_bool());
|
||||
assert_eq!(false, Literal::from(0).as_bool());
|
||||
assert_eq!(true, Literal::from(1).as_bool());
|
||||
assert_eq!(false, Literal::from(-1).as_bool());
|
||||
assert_eq!(true, Literal::from(1.1).as_bool());
|
||||
assert_eq!(false, Literal::from(-1.1).as_bool());
|
||||
assert_eq!(true, Literal::from("true").as_bool());
|
||||
assert_eq!(false, Literal::from("false").as_bool());
|
||||
assert_eq!(true, Literal::from("falsey").as_bool());
|
||||
assert_eq!(true, Literal::from("something").as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_int() {
|
||||
assert_eq!(0, Literal::None.as_int());
|
||||
assert_eq!(0, Literal::Null.as_int());
|
||||
assert_eq!(0, Literal::Void.as_int());
|
||||
assert_eq!(1, Literal::True.as_int());
|
||||
assert_eq!(0, Literal::False.as_int());
|
||||
assert_eq!(0, Literal::from(0).as_int());
|
||||
assert_eq!(1, Literal::from(1).as_int());
|
||||
assert_eq!(-1, Literal::from(-1).as_int());
|
||||
assert_eq!(1, Literal::from(1.1).as_int());
|
||||
assert_eq!(-1, Literal::from(-1.1).as_int());
|
||||
assert_eq!(3, Literal::from("3").as_int());
|
||||
assert_eq!(0, Literal::from("true").as_int());
|
||||
assert_eq!(0, Literal::from("false").as_int());
|
||||
assert_eq!(0, Literal::from("something").as_int());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_float() {
|
||||
assert_eq!(0.0, Literal::None.as_float());
|
||||
assert_eq!(0.0, Literal::Null.as_float());
|
||||
assert_eq!(0.0, Literal::Void.as_float());
|
||||
assert_eq!(1.0, Literal::True.as_float());
|
||||
assert_eq!(0.0, Literal::False.as_float());
|
||||
assert_eq!(0.0, Literal::from(0).as_float());
|
||||
assert_eq!(1.0, Literal::from(1).as_float());
|
||||
assert_eq!(-1.0, Literal::from(-1).as_float());
|
||||
assert_eq!(1.1, Literal::from(1.1).as_float());
|
||||
assert_eq!(-1.1, Literal::from(-1.1).as_float());
|
||||
assert_eq!(3.0, Literal::from("3").as_float());
|
||||
assert_eq!(0.0, Literal::from("true").as_float());
|
||||
assert_eq!(0.0, Literal::from("false").as_float());
|
||||
assert_eq!(0.0, Literal::from("something").as_float());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_number() {
|
||||
assert_eq!(Number::from(0), Literal::None.as_number());
|
||||
assert_eq!(Number::from(0), Literal::Null.as_number());
|
||||
assert_eq!(Number::from(0), Literal::Void.as_number());
|
||||
assert_eq!(Number::from(1), Literal::True.as_number());
|
||||
assert_eq!(Number::from(0), Literal::False.as_number());
|
||||
assert_eq!(Number::from(0), Literal::from(0).as_number());
|
||||
assert_eq!(Number::from(1), Literal::from(1).as_number());
|
||||
assert_eq!(Number::from(-1), Literal::from(-1).as_number());
|
||||
assert_eq!(Number::from(1.1), Literal::from(1.1).as_number());
|
||||
assert_eq!(Number::from(-1.1), Literal::from(-1.1).as_number());
|
||||
assert_eq!(Number::from(3), Literal::from("3").as_number());
|
||||
assert_eq!(Number::from(0), Literal::from("true").as_number());
|
||||
assert_eq!(Number::from(0), Literal::from("false").as_number());
|
||||
assert_eq!(Number::from(0), Literal::from("something").as_number());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_strand() {
|
||||
assert_eq!(Strand::from("NONE"), Literal::None.as_strand());
|
||||
assert_eq!(Strand::from("NULL"), Literal::Null.as_strand());
|
||||
assert_eq!(Strand::from("VOID"), Literal::Void.as_strand());
|
||||
assert_eq!(Strand::from("true"), Literal::True.as_strand());
|
||||
assert_eq!(Strand::from("false"), Literal::False.as_strand());
|
||||
assert_eq!(Strand::from("0"), Literal::from(0).as_strand());
|
||||
assert_eq!(Strand::from("1"), Literal::from(1).as_strand());
|
||||
assert_eq!(Strand::from("-1"), Literal::from(-1).as_strand());
|
||||
assert_eq!(Strand::from("1.1"), Literal::from(1.1).as_strand());
|
||||
assert_eq!(Strand::from("-1.1"), Literal::from(-1.1).as_strand());
|
||||
assert_eq!(Strand::from("3"), Literal::from("3").as_strand());
|
||||
assert_eq!(Strand::from("true"), Literal::from("true").as_strand());
|
||||
assert_eq!(Strand::from("false"), Literal::from("false").as_strand());
|
||||
assert_eq!(Strand::from("something"), Literal::from("something").as_strand());
|
||||
}
|
||||
}
|
|
@ -12,27 +12,28 @@ pub mod duration;
|
|||
pub mod expression;
|
||||
pub mod fetch;
|
||||
pub mod field;
|
||||
pub mod filter;
|
||||
pub mod function;
|
||||
pub mod geometry;
|
||||
pub mod graph;
|
||||
pub mod group;
|
||||
pub mod ident;
|
||||
pub mod idiom;
|
||||
pub mod kind;
|
||||
pub mod limit;
|
||||
pub mod literal;
|
||||
pub mod model;
|
||||
pub mod number;
|
||||
pub mod object;
|
||||
pub mod operation;
|
||||
pub mod operator;
|
||||
pub mod order;
|
||||
pub mod output;
|
||||
pub mod param;
|
||||
pub mod parser;
|
||||
pub mod part;
|
||||
pub mod permission;
|
||||
pub mod point;
|
||||
pub mod polygon;
|
||||
pub mod query;
|
||||
pub mod regex;
|
||||
pub mod script;
|
||||
pub mod split;
|
||||
pub mod start;
|
||||
pub mod statement;
|
||||
|
@ -45,3 +46,6 @@ pub mod timeout;
|
|||
pub mod value;
|
||||
pub mod version;
|
||||
pub mod view;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test;
|
||||
|
|
|
@ -1,146 +1,335 @@
|
|||
use dec::prelude::FromPrimitive;
|
||||
use dec::prelude::ToPrimitive;
|
||||
use dec::Decimal;
|
||||
use dec::MathematicalOps;
|
||||
use nom::number::complete::double;
|
||||
use nom::IResult;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::iter::Product;
|
||||
use std::iter::Sum;
|
||||
use std::ops;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize)]
|
||||
pub struct Number {
|
||||
pub value: Decimal,
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub enum Number {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Decimal(Decimal),
|
||||
}
|
||||
|
||||
impl Default for Number {
|
||||
fn default() -> Self {
|
||||
Number {
|
||||
value: 0i32.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for Number {
|
||||
fn from(i: i8) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i16> for Number {
|
||||
fn from(i: i16) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Number {
|
||||
fn from(i: i32) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Number {
|
||||
fn from(i: i64) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Number {
|
||||
fn from(i: isize) -> Self {
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Number {
|
||||
fn from(i: u8) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Number {
|
||||
fn from(i: u16) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Number {
|
||||
fn from(i: u32) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
}
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Number {
|
||||
fn from(i: u64) -> Self {
|
||||
Number {
|
||||
value: i.into(),
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Number {
|
||||
fn from(i: usize) -> Self {
|
||||
Number::Decimal(Decimal::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Number {
|
||||
fn from(f: f32) -> Self {
|
||||
Number {
|
||||
value: Decimal::from_str(&f.to_string()).unwrap_or(Decimal::new(0, 0)),
|
||||
}
|
||||
Number::Decimal(Decimal::from_f64(f as f64).unwrap_or(Decimal::new(0, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
fn from(f: f64) -> Self {
|
||||
Number {
|
||||
value: Decimal::from_str(&f.to_string()).unwrap_or(Decimal::new(0, 0)),
|
||||
}
|
||||
Number::Decimal(Decimal::from_f64(f as f64).unwrap_or(Decimal::new(0, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Number {
|
||||
fn from(s: &str) -> Self {
|
||||
Number {
|
||||
value: Decimal::from_str(s).unwrap_or(Decimal::new(0, 0)),
|
||||
}
|
||||
Number::Decimal(Decimal::from_str(s).unwrap_or(Decimal::new(0, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Number {
|
||||
fn from(s: String) -> Self {
|
||||
Number {
|
||||
value: Decimal::from_str(&s).unwrap_or(Decimal::new(0, 0)),
|
||||
}
|
||||
Number::Decimal(Decimal::from_str(&s).unwrap_or(Decimal::new(0, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Decimal> for Number {
|
||||
fn from(v: Decimal) -> Self {
|
||||
Number {
|
||||
value: v,
|
||||
}
|
||||
Number::Decimal(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
match self {
|
||||
Number::Int(v) => write!(f, "{}", v),
|
||||
Number::Float(v) => write!(f, "{}", v),
|
||||
Number::Decimal(v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Number {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_some(&self.value)
|
||||
if s.is_human_readable() {
|
||||
match self {
|
||||
Number::Int(v) => s.serialize_i64(*v),
|
||||
Number::Float(v) => s.serialize_f64(*v),
|
||||
Number::Decimal(v) => s.serialize_some(v),
|
||||
}
|
||||
} else {
|
||||
let mut val = serializer.serialize_struct("Number", 1)?;
|
||||
val.serialize_field("value", &self.value)?;
|
||||
val.end()
|
||||
match self {
|
||||
Number::Int(v) => s.serialize_newtype_variant("Number", 0, "Int", v),
|
||||
Number::Float(v) => s.serialize_newtype_variant("Number", 1, "Float", v),
|
||||
Number::Decimal(v) => s.serialize_newtype_variant("Number", 2, "Decimal", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
// -----------------------------------
|
||||
// Simple number detection
|
||||
// -----------------------------------
|
||||
|
||||
pub fn is_truthy(&self) -> bool {
|
||||
match self {
|
||||
Number::Int(v) => v != &0,
|
||||
Number::Float(v) => v != &0.0,
|
||||
Number::Decimal(v) => v != &Decimal::new(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// Simple conversion of number
|
||||
// -----------------------------------
|
||||
|
||||
pub fn as_int(self) -> i64 {
|
||||
match self {
|
||||
Number::Int(v) => v,
|
||||
Number::Float(v) => v as i64,
|
||||
Number::Decimal(v) => v.to_i64().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(self) -> f64 {
|
||||
match self {
|
||||
Number::Int(v) => v as f64,
|
||||
Number::Float(v) => v,
|
||||
Number::Decimal(v) => v.to_f64().unwrap_or(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_decimal(self) -> Decimal {
|
||||
match self {
|
||||
Number::Int(v) => Decimal::from(v),
|
||||
Number::Float(v) => Decimal::from_f64(v).unwrap_or(Decimal::new(0, 0)),
|
||||
Number::Decimal(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
pub fn to_usize(&self) -> usize {
|
||||
match self {
|
||||
Number::Int(v) => *v as usize,
|
||||
Number::Float(v) => *v as usize,
|
||||
Number::Decimal(v) => v.to_usize().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_int(&self) -> i64 {
|
||||
match self {
|
||||
Number::Int(v) => *v,
|
||||
Number::Float(v) => *v as i64,
|
||||
Number::Decimal(v) => v.to_i64().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_float(&self) -> f64 {
|
||||
match self {
|
||||
Number::Int(v) => *v as f64,
|
||||
Number::Float(v) => *v,
|
||||
Number::Decimal(v) => v.to_f64().unwrap_or(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_decimal(&self) -> Decimal {
|
||||
match self {
|
||||
Number::Int(v) => Decimal::from(*v),
|
||||
Number::Float(v) => Decimal::from_f64(*v).unwrap_or(Decimal::new(0, 0)),
|
||||
Number::Decimal(v) => *v,
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
pub fn abs(self) -> Self {
|
||||
match self {
|
||||
Number::Int(v) => v.abs().into(),
|
||||
Number::Float(v) => v.abs().into(),
|
||||
Number::Decimal(v) => v.abs().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceil(self) -> Self {
|
||||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.ceil().into(),
|
||||
Number::Decimal(v) => v.ceil().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floor(self) -> Self {
|
||||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.floor().into(),
|
||||
Number::Decimal(v) => v.floor().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn round(self) -> Self {
|
||||
match self {
|
||||
Number::Int(v) => v.into(),
|
||||
Number::Float(v) => v.round().into(),
|
||||
Number::Decimal(v) => v.round().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sqrt(self) -> Self {
|
||||
match self {
|
||||
Number::Int(v) => (v as f64).sqrt().into(),
|
||||
Number::Float(v) => v.sqrt().into(),
|
||||
Number::Decimal(v) => v.sqrt().unwrap_or(Decimal::new(0, 0)).into(),
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
pub fn fixed(self, precision: usize) -> Number {
|
||||
match self {
|
||||
Number::Int(v) => format!("{:.1$}", v, precision).into(),
|
||||
Number::Float(v) => format!("{:.1$}", v, precision).into(),
|
||||
Number::Decimal(v) => v.round_dp(precision as u32).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Number {}
|
||||
|
||||
impl Ord for Number {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(&other).unwrap_or(Ordering::Equal)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Number {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => v.eq(w),
|
||||
(Number::Float(v), Number::Float(w)) => v.eq(w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => v.eq(w),
|
||||
// ------------------------------
|
||||
(Number::Int(v), Number::Float(w)) => (*v as f64).eq(w),
|
||||
(Number::Float(v), Number::Int(w)) => v.eq(&(*w as f64)),
|
||||
// ------------------------------
|
||||
(Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).eq(w),
|
||||
(Number::Decimal(v), Number::Int(w)) => v.eq(&Decimal::from(*w)),
|
||||
// ------------------------------
|
||||
(Number::Float(v), Number::Decimal(w)) => {
|
||||
Decimal::from_f64(*v).unwrap_or(Decimal::default()).eq(w)
|
||||
}
|
||||
(Number::Decimal(v), Number::Float(w)) => {
|
||||
v.eq(&Decimal::from_f64(*w).unwrap_or(Decimal::default()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Number {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => v.partial_cmp(w),
|
||||
(Number::Float(v), Number::Float(w)) => v.partial_cmp(w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => v.partial_cmp(w),
|
||||
// ------------------------------
|
||||
(Number::Int(v), Number::Float(w)) => (*v as f64).partial_cmp(w),
|
||||
(Number::Float(v), Number::Int(w)) => v.partial_cmp(&(*w as f64)),
|
||||
// ------------------------------
|
||||
(Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).partial_cmp(w),
|
||||
(Number::Decimal(v), Number::Int(w)) => v.partial_cmp(&Decimal::from(*w)),
|
||||
// ------------------------------
|
||||
(Number::Float(v), Number::Decimal(w)) => {
|
||||
Decimal::from_f64(*v).unwrap_or(Decimal::default()).partial_cmp(w)
|
||||
}
|
||||
(Number::Decimal(v), Number::Float(w)) => {
|
||||
v.partial_cmp(&Decimal::from_f64(*w).unwrap_or(Decimal::default()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,28 +337,150 @@ impl Serialize for Number {
|
|||
impl ops::Add for Number {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Number::from(self.value + other.value)
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v + w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v + w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v + w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(v as f64 + w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v + w as f64),
|
||||
(v, w) => Number::from(v.as_decimal() + w.as_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ops::Add<&'b Number> for &'a Number {
|
||||
type Output = Number;
|
||||
fn add(self, other: &'b Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v + w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v + w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v + w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 + w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v + *w as f64),
|
||||
(v, w) => Number::from(v.to_decimal() + w.to_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for Number {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Number::from(self.value - other.value)
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v - w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v - w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v - w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(v as f64 - w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v - w as f64),
|
||||
(v, w) => Number::from(v.as_decimal() - w.as_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ops::Sub<&'b Number> for &'a Number {
|
||||
type Output = Number;
|
||||
fn sub(self, other: &'b Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v - w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v - w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v - w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 - w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v - *w as f64),
|
||||
(v, w) => Number::from(v.to_decimal() - w.to_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for Number {
|
||||
type Output = Self;
|
||||
fn mul(self, other: Self) -> Self {
|
||||
Number::from(self.value * other.value)
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v * w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v * w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v * w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(v as f64 * w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v * w as f64),
|
||||
(v, w) => Number::from(v.as_decimal() * w.as_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ops::Mul<&'b Number> for &'a Number {
|
||||
type Output = Number;
|
||||
fn mul(self, other: &'b Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v * w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v * w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v * w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 * w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v * *w as f64),
|
||||
(v, w) => Number::from(v.to_decimal() * w.to_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Div for Number {
|
||||
type Output = Self;
|
||||
fn div(self, other: Self) -> Self {
|
||||
Number::from(self.value / other.value)
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v / w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v / w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(v as f64 / w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v / w as f64),
|
||||
(v, w) => Number::from(v.as_decimal() / w.as_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> ops::Div<&'b Number> for &'a Number {
|
||||
type Output = Number;
|
||||
fn div(self, other: &'b Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::Int(v), Number::Int(w)) => Number::Int(v / w),
|
||||
(Number::Float(v), Number::Float(w)) => Number::Float(v / w),
|
||||
(Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w),
|
||||
(Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 / w),
|
||||
(Number::Float(v), Number::Int(w)) => Number::Float(v / *w as f64),
|
||||
(v, w) => Number::from(v.to_decimal() / w.to_decimal()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
impl Sum<Self> for Number {
|
||||
fn sum<I>(iter: I) -> Number
|
||||
where
|
||||
I: Iterator<Item = Self>,
|
||||
{
|
||||
iter.fold(Number::Int(0), |a, b| a + b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Self> for Number {
|
||||
fn sum<I>(iter: I) -> Number
|
||||
where
|
||||
I: Iterator<Item = &'a Self>,
|
||||
{
|
||||
iter.fold(Number::Int(0), |a, b| &a + b)
|
||||
}
|
||||
}
|
||||
|
||||
impl Product<Self> for Number {
|
||||
fn product<I>(iter: I) -> Number
|
||||
where
|
||||
I: Iterator<Item = Self>,
|
||||
{
|
||||
iter.fold(Number::Int(1), |a, b| a * b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Product<&'a Self> for Number {
|
||||
fn product<I>(iter: I) -> Number
|
||||
where
|
||||
I: Iterator<Item = &'a Self>,
|
||||
{
|
||||
iter.fold(Number::Int(1), |a, b| &a * b)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::{commas, escape, val_char};
|
||||
use crate::sql::expression::expression;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
|
@ -19,13 +17,40 @@ use nom::IResult;
|
|||
use serde::ser::SerializeMap;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
const NAME: &'static str = "Object";
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
|
||||
pub struct Object {
|
||||
pub value: Vec<(String, Value)>,
|
||||
pub value: BTreeMap<String, Value>,
|
||||
}
|
||||
|
||||
impl From<BTreeMap<String, Value>> for Object {
|
||||
fn from(v: BTreeMap<String, Value>) -> Self {
|
||||
Object {
|
||||
value: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HashMap<String, Value>> for Object {
|
||||
fn from(v: HashMap<String, Value>) -> Self {
|
||||
Object {
|
||||
value: v.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn remove(&mut self, key: &String) {
|
||||
self.value.remove(key);
|
||||
()
|
||||
}
|
||||
pub fn insert(&mut self, key: &String, val: Value) {
|
||||
self.value.insert(key.to_owned(), val);
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Object {
|
||||
|
@ -46,18 +71,19 @@ impl dbs::Process for Object {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
self.value
|
||||
.iter()
|
||||
.map(|(k, v)| match v.process(ctx, exe, doc) {
|
||||
Ok(v) => Ok((k.clone(), Value::from(v))),
|
||||
.map(|(k, v)| match v.process(ctx, opt, exe, doc) {
|
||||
Ok(v) => Ok((k.clone(), v)),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.collect::<Result<BTreeMap<_, _>, _>>()
|
||||
.map(|v| {
|
||||
Literal::Object(Object {
|
||||
Value::Object(Object {
|
||||
value: v,
|
||||
})
|
||||
})
|
||||
|
@ -77,7 +103,7 @@ impl Serialize for Object {
|
|||
}
|
||||
map.end()
|
||||
} else {
|
||||
let mut val = serializer.serialize_struct(NAME, 1)?;
|
||||
let mut val = serializer.serialize_struct("Object", 1)?;
|
||||
val.serialize_field("value", &self.value)?;
|
||||
val.end()
|
||||
}
|
||||
|
@ -95,7 +121,7 @@ pub fn object(i: &str) -> IResult<&str, Object> {
|
|||
Ok((
|
||||
i,
|
||||
Object {
|
||||
value: v,
|
||||
value: v.into_iter().collect(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -105,8 +131,8 @@ fn item(i: &str) -> IResult<&str, (String, Value)> {
|
|||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(":")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
Ok((i, (String::from(k), Value::from(v))))
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, (String::from(k), v)))
|
||||
}
|
||||
|
||||
fn key(i: &str) -> IResult<&str, &str> {
|
||||
|
@ -118,11 +144,11 @@ fn key_none(i: &str) -> IResult<&str, &str> {
|
|||
}
|
||||
|
||||
fn key_single(i: &str) -> IResult<&str, &str> {
|
||||
delimited(tag("\""), is_not("\""), tag("\""))(i)
|
||||
delimited(tag("\'"), is_not("\'"), tag("\'"))(i)
|
||||
}
|
||||
|
||||
fn key_double(i: &str) -> IResult<&str, &str> {
|
||||
delimited(tag("\'"), is_not("\'"), tag("\'"))(i)
|
||||
delimited(tag("\""), is_not("\""), tag("\""))(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -136,7 +162,7 @@ mod tests {
|
|||
let res = object(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("{ one: 1, two: 2, tre: 3 }", format!("{}", out));
|
||||
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
|
||||
assert_eq!(out.value.len(), 3);
|
||||
}
|
||||
|
||||
|
@ -146,7 +172,7 @@ mod tests {
|
|||
let res = object(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("{ one: 1, two: 2, tre: 3 }", format!("{}", out));
|
||||
assert_eq!("{ one: 1, tre: 3, two: 2 }", format!("{}", out));
|
||||
assert_eq!(out.value.len(), 3);
|
||||
}
|
||||
|
||||
|
@ -156,7 +182,7 @@ mod tests {
|
|||
let res = object(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("{ one: 1, two: 2, tre: 3 + 1 }", format!("{}", out));
|
||||
assert_eq!("{ one: 1, tre: 3 + 1, two: 2 }", format!("{}", out));
|
||||
assert_eq!(out.value.len(), 3);
|
||||
}
|
||||
}
|
||||
|
|
13
src/sql/operation.rs
Normal file
13
src/sql/operation.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::sql::value::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Operations(pub Vec<Operation>);
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Operation {
|
||||
pub op: String,
|
||||
pub prev: Option<String>,
|
||||
pub path: String,
|
||||
pub value: Value,
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
|
@ -18,8 +20,9 @@ pub enum Operator {
|
|||
Inc, // +=
|
||||
Dec, // -=
|
||||
//
|
||||
Equal, // =
|
||||
Exact, // ==
|
||||
//
|
||||
Equal, // =
|
||||
NotEqual, // !=
|
||||
AllEqual, // *=
|
||||
AnyEqual, // ?=
|
||||
|
@ -37,12 +40,12 @@ pub enum Operator {
|
|||
Contain, // ∋
|
||||
NotContain, // ∌
|
||||
ContainAll, // ⊇
|
||||
ContainSome, // ⊃
|
||||
ContainAny, // ⊃
|
||||
ContainNone, // ⊅
|
||||
Inside, // ∈
|
||||
NotInside, // ∉
|
||||
AllInside, // ⊆
|
||||
SomeInside, // ⊂
|
||||
AnyInside, // ⊂
|
||||
NoneInside, // ⊄
|
||||
Intersects, // ∩
|
||||
}
|
||||
|
@ -64,8 +67,8 @@ impl fmt::Display for Operator {
|
|||
Operator::Div => write!(f, "/"),
|
||||
Operator::Inc => write!(f, "+="),
|
||||
Operator::Dec => write!(f, "-="),
|
||||
Operator::Equal => write!(f, "="),
|
||||
Operator::Exact => write!(f, "=="),
|
||||
Operator::Equal => write!(f, "="),
|
||||
Operator::NotEqual => write!(f, "!="),
|
||||
Operator::AllEqual => write!(f, "*="),
|
||||
Operator::AnyEqual => write!(f, "?="),
|
||||
|
@ -80,12 +83,12 @@ impl fmt::Display for Operator {
|
|||
Operator::Contain => write!(f, "CONTAINS"),
|
||||
Operator::NotContain => write!(f, "CONTAINS NOT"),
|
||||
Operator::ContainAll => write!(f, "CONTAINS ALL"),
|
||||
Operator::ContainSome => write!(f, "CONTAINS SOME"),
|
||||
Operator::ContainAny => write!(f, "CONTAINS ANY"),
|
||||
Operator::ContainNone => write!(f, "CONTAINS NONE"),
|
||||
Operator::Inside => write!(f, "INSIDE"),
|
||||
Operator::NotInside => write!(f, "NOT INSIDE"),
|
||||
Operator::AllInside => write!(f, "ALL INSIDE"),
|
||||
Operator::SomeInside => write!(f, "SOME INSIDE"),
|
||||
Operator::AnyInside => write!(f, "ANY INSIDE"),
|
||||
Operator::NoneInside => write!(f, "NONE INSIDE"),
|
||||
Operator::Intersects => write!(f, "INTERSECTS"),
|
||||
}
|
||||
|
@ -101,7 +104,12 @@ pub fn assigner(i: &str) -> IResult<&str, Operator> {
|
|||
}
|
||||
|
||||
pub fn operator(i: &str) -> IResult<&str, Operator> {
|
||||
alt((
|
||||
alt((symbols, phrases))(i)
|
||||
}
|
||||
|
||||
pub fn symbols(i: &str) -> IResult<&str, Operator> {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
alt((
|
||||
map(tag("=="), |_| Operator::Exact),
|
||||
map(tag("!="), |_| Operator::NotEqual),
|
||||
|
@ -136,12 +144,20 @@ pub fn operator(i: &str) -> IResult<&str, Operator> {
|
|||
map(tag("∈"), |_| Operator::Inside),
|
||||
map(tag("∉"), |_| Operator::NotInside),
|
||||
map(tag("⊇"), |_| Operator::ContainAll),
|
||||
map(tag("⊃"), |_| Operator::ContainSome),
|
||||
map(tag("⊃"), |_| Operator::ContainAny),
|
||||
map(tag("⊅"), |_| Operator::ContainNone),
|
||||
map(tag("⊆"), |_| Operator::AllInside),
|
||||
map(tag("⊂"), |_| Operator::SomeInside),
|
||||
map(tag("⊂"), |_| Operator::AnyInside),
|
||||
map(tag("⊄"), |_| Operator::NoneInside),
|
||||
)),
|
||||
))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
||||
pub fn phrases(i: &str) -> IResult<&str, Operator> {
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = alt((
|
||||
alt((
|
||||
map(tag_no_case("&&"), |_| Operator::And),
|
||||
map(tag_no_case("AND"), |_| Operator::And),
|
||||
|
@ -154,17 +170,19 @@ pub fn operator(i: &str) -> IResult<&str, Operator> {
|
|||
)),
|
||||
alt((
|
||||
map(tag_no_case("CONTAINS ALL"), |_| Operator::ContainAll),
|
||||
map(tag_no_case("CONTAINS ANY"), |_| Operator::ContainAny),
|
||||
map(tag_no_case("CONTAINS NONE"), |_| Operator::ContainNone),
|
||||
map(tag_no_case("CONTAINS SOME"), |_| Operator::ContainSome),
|
||||
map(tag_no_case("CONTAINS NOT"), |_| Operator::NotContain),
|
||||
map(tag_no_case("CONTAINS"), |_| Operator::Contain),
|
||||
map(tag_no_case("ALL INSIDE"), |_| Operator::AllInside),
|
||||
map(tag_no_case("ANY INSIDE"), |_| Operator::AnyInside),
|
||||
map(tag_no_case("NONE INSIDE"), |_| Operator::NoneInside),
|
||||
map(tag_no_case("SOME INSIDE"), |_| Operator::SomeInside),
|
||||
map(tag_no_case("NOT INSIDE"), |_| Operator::NotInside),
|
||||
map(tag_no_case("INSIDE"), |_| Operator::Inside),
|
||||
map(tag_no_case("OUTSIDE"), |_| Operator::NotInside),
|
||||
map(tag_no_case("INTERSECTS"), |_| Operator::Intersects),
|
||||
)),
|
||||
))(i)
|
||||
))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
Ok((i, v))
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Orders(Vec<Order>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Orders(pub Vec<Order>);
|
||||
|
||||
impl fmt::Display for Orders {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -23,7 +23,7 @@ impl fmt::Display for Orders {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Order {
|
||||
pub order: Idiom,
|
||||
pub random: bool,
|
||||
|
@ -98,6 +98,7 @@ fn order_raw(i: &str) -> IResult<&str, Order> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn order_statement() {
|
||||
|
@ -108,7 +109,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: false,
|
||||
|
@ -127,7 +128,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: false,
|
||||
|
@ -166,14 +167,14 @@ mod tests {
|
|||
out,
|
||||
Orders(vec![
|
||||
Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: false,
|
||||
direction: true,
|
||||
},
|
||||
Order {
|
||||
order: Idiom::from("other.field"),
|
||||
order: Idiom::parse("other.field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: false,
|
||||
|
@ -193,7 +194,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: true,
|
||||
numeric: false,
|
||||
|
@ -212,7 +213,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: true,
|
||||
|
@ -231,7 +232,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: false,
|
||||
numeric: false,
|
||||
|
@ -250,7 +251,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Orders(vec![Order {
|
||||
order: Idiom::from("field"),
|
||||
order: Idiom::parse("field"),
|
||||
random: false,
|
||||
collate: true,
|
||||
numeric: true,
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::idiom;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -24,14 +26,6 @@ impl From<Idiom> for Param {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Param {
|
||||
fn from(p: &str) -> Param {
|
||||
Param {
|
||||
name: Idiom::from(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Param {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "${}", &self.name)
|
||||
|
@ -42,20 +36,32 @@ impl dbs::Process for Param {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
// 1. Loop through the context variables
|
||||
// 2. Find a variable with the right name
|
||||
// 3. Process the variable value
|
||||
// 4. Return the processed value
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Find a base variable by name
|
||||
match self.name.parts.first() {
|
||||
// The first part will be a field
|
||||
Some(Part::Field(v)) => match ctx.value::<Value>(v.name.clone()) {
|
||||
// The base variable exists
|
||||
Some(v) => {
|
||||
// Process the paramater value
|
||||
let res = v.process(ctx, opt, exe, doc)?;
|
||||
// Return the desired field
|
||||
res.get(ctx, opt, exe, &self.name.next()).ok()
|
||||
}
|
||||
// The base variable does not exist
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn param(i: &str) -> IResult<&str, Param> {
|
||||
let (i, _) = tag("$")(i)?;
|
||||
let (i, v) = idiom(i)?;
|
||||
let (i, v) = idiom::param(i)?;
|
||||
Ok((i, Param::from(v)))
|
||||
}
|
||||
|
||||
|
@ -63,6 +69,7 @@ pub fn param(i: &str) -> IResult<&str, Param> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn param_normal() {
|
||||
|
@ -71,7 +78,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$test", format!("{}", out));
|
||||
assert_eq!(out, Param::from("test"));
|
||||
assert_eq!(out, Param::parse("$test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -81,7 +88,7 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$test_and_deliver", format!("{}", out));
|
||||
assert_eq!(out, Param::from("test_and_deliver"));
|
||||
assert_eq!(out, Param::parse("$test_and_deliver"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -91,6 +98,6 @@ mod tests {
|
|||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("$test.temporary[0].embedded", format!("{}", out));
|
||||
assert_eq!(out, Param::from("test.temporary[0].embedded"));
|
||||
assert_eq!(out, Param::parse("$test.temporary[0].embedded"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ mod tests {
|
|||
let sql = " SELECT * FROM { }} ";
|
||||
let res = parse(sql);
|
||||
assert!(res.is_err());
|
||||
println!("{}", res.err().unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -81,7 +80,7 @@ mod tests {
|
|||
{ key: (3 + 1 + 2), other: 9 * 7, 'some thing': { otherkey: 'text', } } AS object
|
||||
FROM $param, test, temp, test:thingy, |test:10|, |test:1..10|
|
||||
WHERE IF true THEN 'YAY' ELSE 'OOPS' END
|
||||
AND (0.1341, 0.5719) INSIDE ( (0.1341, 0.5719), (0.1341, 0.5719) )
|
||||
AND (0.1341, 0.5719) INSIDE { type: 'Polygon', coordinates: [[[0.1341, 0.5719], [0.1341, 0.5719]]] }
|
||||
AND (3 + 3 * 4)=6
|
||||
AND 3 + 3 * 4 = 6
|
||||
AND ages CONTAINS 18
|
||||
|
|
181
src/sql/part.rs
Normal file
181
src/sql/part.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::graph::{graph as graph_raw, Graph};
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::number::{number, Number};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum Part {
|
||||
All,
|
||||
Last,
|
||||
First,
|
||||
Field(Ident),
|
||||
Index(Number),
|
||||
Where(Value),
|
||||
Graph(Graph),
|
||||
}
|
||||
|
||||
impl From<Number> for Part {
|
||||
fn from(v: Number) -> Self {
|
||||
Part::Index(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ident> for Part {
|
||||
fn from(v: Ident) -> Self {
|
||||
Part::Field(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for Part {
|
||||
fn from(v: Value) -> Self {
|
||||
Part::Where(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Graph> for Part {
|
||||
fn from(v: Graph) -> Self {
|
||||
Part::Graph(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Part {
|
||||
fn from(v: String) -> Self {
|
||||
Part::Field(Ident::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Part {
|
||||
fn from(v: &str) -> Self {
|
||||
Part::Field(Ident::from(v.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Part {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Part::All => write!(f, "[*]"),
|
||||
Part::Last => write!(f, "[$]"),
|
||||
Part::First => write!(f, "[0]"),
|
||||
Part::Field(v) => write!(f, ".{}", v),
|
||||
Part::Index(v) => write!(f, "[{}]", v),
|
||||
Part::Where(v) => write!(f, "[WHERE {}]", v),
|
||||
Part::Graph(v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn part(i: &str) -> IResult<&str, Part> {
|
||||
alt((all, last, index, field, graph, filter))(i)
|
||||
}
|
||||
|
||||
pub fn first(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = ident(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
||||
pub fn all(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = tag("*")(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, Part::All))
|
||||
}
|
||||
|
||||
pub fn last(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = tag("$")(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, Part::Last))
|
||||
}
|
||||
|
||||
pub fn index(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, v) = number(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, Part::Index(v)))
|
||||
}
|
||||
|
||||
pub fn field(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = tag(".")(i)?;
|
||||
let (i, v) = ident(i)?;
|
||||
Ok((i, Part::Field(v)))
|
||||
}
|
||||
|
||||
pub fn filter(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = tag("[")(i)?;
|
||||
let (i, _) = alt((tag_no_case("WHERE"), tag("?")))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
let (i, _) = tag("]")(i)?;
|
||||
Ok((i, Part::Where(v)))
|
||||
}
|
||||
|
||||
pub fn graph(i: &str) -> IResult<&str, Part> {
|
||||
let (i, v) = graph_raw(i)?;
|
||||
Ok((i, Part::Graph(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn part_all() {
|
||||
let sql = "[*]";
|
||||
let res = part(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[*]", format!("{}", out));
|
||||
assert_eq!(out, Part::All);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part_last() {
|
||||
let sql = "[$]";
|
||||
let res = part(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[$]", format!("{}", out));
|
||||
assert_eq!(out, Part::Last);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part_number() {
|
||||
let sql = "[0]";
|
||||
let res = part(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[0]", format!("{}", out));
|
||||
assert_eq!(out, Part::Index(Number::from("0")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part_expression_question() {
|
||||
let sql = "[? test = true]";
|
||||
let res = part(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[WHERE test = true]", format!("{}", out));
|
||||
assert_eq!(out, Part::Where(Value::from(Expression::parse("test = true"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part_expression_condition() {
|
||||
let sql = "[WHERE test = true]";
|
||||
let res = part(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("[WHERE test = true]", format!("{}", out));
|
||||
assert_eq!(out, Part::Where(Value::from(Expression::parse("test = true"))));
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::map;
|
||||
|
@ -137,7 +137,7 @@ pub enum Permission {
|
|||
Create,
|
||||
Update,
|
||||
Delete,
|
||||
Specific(Expression),
|
||||
Specific(Value),
|
||||
}
|
||||
|
||||
impl Default for Permission {
|
||||
|
@ -173,7 +173,7 @@ fn permission(i: &str) -> IResult<&str, Vec<(Permission, Permission)>> {
|
|||
let (i, expr) = alt((
|
||||
map(tag_no_case("NONE"), |_| Permission::None),
|
||||
map(tag_no_case("FULL"), |_| Permission::Full),
|
||||
map(tuple((tag_no_case("WHERE"), shouldbespace, expression)), |(_, _, v)| {
|
||||
map(tuple((tag_no_case("WHERE"), shouldbespace, value)), |(_, _, v)| {
|
||||
Permission::Specific(v)
|
||||
}),
|
||||
))(i)?;
|
||||
|
@ -184,6 +184,8 @@ fn permission(i: &str) -> IResult<&str, Vec<(Permission, Permission)>> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn permissions_none() {
|
||||
|
@ -220,8 +222,8 @@ mod tests {
|
|||
out,
|
||||
Permissions {
|
||||
select: Permission::Full,
|
||||
create: Permission::Specific(Expression::from("public = true")),
|
||||
update: Permission::Specific(Expression::from("public = true")),
|
||||
create: Permission::Specific(Value::from(Expression::parse("public = true"))),
|
||||
update: Permission::Specific(Value::from(Expression::parse("public = true"))),
|
||||
delete: Permission::None,
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::number::{number, Number};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::IResult;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::ser::SerializeTupleStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
|
||||
pub struct Point(Number, Number);
|
||||
|
||||
impl fmt::Display for Point {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Point {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
let mut arr = serializer.serialize_seq(Some(2))?;
|
||||
arr.serialize_element(&self.0)?;
|
||||
arr.serialize_element(&self.1)?;
|
||||
arr.end()
|
||||
} else {
|
||||
let mut val = serializer.serialize_tuple_struct("Point", 2)?;
|
||||
val.serialize_field(&self.0)?;
|
||||
val.serialize_field(&self.1)?;
|
||||
val.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point(i: &str) -> IResult<&str, Point> {
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, lat) = number(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(",")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, lng) = number(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, Point(lat, lng)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn point_simple() {
|
||||
let sql = "(0, 0)";
|
||||
let res = point(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("(0, 0)", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point_complex() {
|
||||
let sql = "(51.509865, -0.118092)";
|
||||
let res = point(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("(51.509865, -0.118092)", format!("{}", out));
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::point::{point, Point};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::IResult;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
|
||||
pub struct Polygon {
|
||||
pub points: Vec<Point>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Polygon {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"( {} )",
|
||||
self.points.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Polygon {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_some(&self.points)
|
||||
} else {
|
||||
let mut val = serializer.serialize_struct("Polygon", 1)?;
|
||||
val.serialize_field("points", &self.points)?;
|
||||
val.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polygon(i: &str) -> IResult<&str, Polygon> {
|
||||
let (i, _) = tag("(")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, v) = separated_list1(commas, point)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Polygon {
|
||||
points: v,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn polygon_simple() {
|
||||
let sql = "( (0, 0), (0, 0), (0, 0) )";
|
||||
let res = polygon(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("( (0, 0), (0, 0), (0, 0) )", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn polygon_complex() {
|
||||
let sql = "( (51.509865, -0.118092), (51.509865, -0.118092), (51.509865, -0.118092) )";
|
||||
let res = polygon(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
"( (51.509865, -0.118092), (51.509865, -0.118092), (51.509865, -0.118092) )",
|
||||
format!("{}", out)
|
||||
);
|
||||
}
|
||||
}
|
127
src/sql/script.rs
Normal file
127
src/sql/script.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use nom::branch::alt;
|
||||
use nom::bytes::complete::escaped;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
const SINGLE: &str = r#"'"#;
|
||||
const SINGLE_ESC: &str = r#"\'"#;
|
||||
|
||||
const DOUBLE: &str = r#"""#;
|
||||
const DOUBLE_ESC: &str = r#"\""#;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Script {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl From<String> for Script {
|
||||
fn from(s: String) -> Self {
|
||||
Script {
|
||||
value: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Script {
|
||||
fn from(s: &str) -> Self {
|
||||
Script {
|
||||
value: String::from(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Script {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.value.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Script {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn script(i: &str) -> IResult<&str, Script> {
|
||||
let (i, v) = script_raw(i)?;
|
||||
Ok((i, Script::from(v)))
|
||||
}
|
||||
|
||||
pub fn script_raw(i: &str) -> IResult<&str, String> {
|
||||
alt((script_single, script_double))(i)
|
||||
}
|
||||
|
||||
fn script_single(i: &str) -> IResult<&str, String> {
|
||||
let (i, _) = tag(SINGLE)(i)?;
|
||||
let (i, v) = alt((escaped(is_not(SINGLE_ESC), '\\', one_of(SINGLE)), tag("")))(i)?;
|
||||
let (i, _) = tag(SINGLE)(i)?;
|
||||
Ok((i, String::from(v).replace(SINGLE_ESC, SINGLE)))
|
||||
}
|
||||
|
||||
fn script_double(i: &str) -> IResult<&str, String> {
|
||||
let (i, _) = tag(DOUBLE)(i)?;
|
||||
let (i, v) = alt((escaped(is_not(DOUBLE_ESC), '\\', one_of(DOUBLE)), tag("")))(i)?;
|
||||
let (i, _) = tag(DOUBLE)(i)?;
|
||||
Ok((i, String::from(v).replace(DOUBLE_ESC, DOUBLE)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn script_empty() {
|
||||
let sql = r#""""#;
|
||||
let res = script(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(r#""""#, format!("{}", out));
|
||||
assert_eq!(out, Script::from(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_single() {
|
||||
let sql = r#"'test'"#;
|
||||
let res = script(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(r#""test""#, format!("{}", out));
|
||||
assert_eq!(out, Script::from("test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_double() {
|
||||
let sql = r#""test""#;
|
||||
let res = script(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(r#""test""#, format!("{}", out));
|
||||
assert_eq!(out, Script::from("test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_quoted_single() {
|
||||
let sql = r#"'te\'st'"#;
|
||||
let res = script(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(r#""te'st""#, format!("{}", out));
|
||||
assert_eq!(out, Script::from(r#"te'st"#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_quoted_double() {
|
||||
let sql = r#""te\"st""#;
|
||||
let res = script(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(r#""te"st""#, format!("{}", out));
|
||||
assert_eq!(out, Script::from(r#"te"st"#));
|
||||
}
|
||||
}
|
|
@ -9,8 +9,8 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Splits(Vec<Split>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Splits(pub Vec<Split>);
|
||||
|
||||
impl fmt::Display for Splits {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -22,7 +22,7 @@ impl fmt::Display for Splits {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Split {
|
||||
pub split: Idiom,
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ fn split_raw(i: &str) -> IResult<&str, Split> {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn split_statement() {
|
||||
|
@ -65,7 +66,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Splits(vec![Split {
|
||||
split: Idiom::from("field")
|
||||
split: Idiom::parse("field")
|
||||
}])
|
||||
);
|
||||
assert_eq!("SPLIT ON field", format!("{}", out));
|
||||
|
@ -80,7 +81,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
out,
|
||||
Splits(vec![Split {
|
||||
split: Idiom::from("field")
|
||||
split: Idiom::parse("field")
|
||||
}])
|
||||
);
|
||||
assert_eq!("SPLIT ON field", format!("{}", out));
|
||||
|
@ -96,10 +97,10 @@ mod tests {
|
|||
out,
|
||||
Splits(vec![
|
||||
Split {
|
||||
split: Idiom::from("field")
|
||||
split: Idiom::parse("field")
|
||||
},
|
||||
Split {
|
||||
split: Idiom::from("other.field")
|
||||
split: Idiom::parse("other.field")
|
||||
},
|
||||
])
|
||||
);
|
||||
|
|
|
@ -8,13 +8,11 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Start {
|
||||
pub expr: u64,
|
||||
}
|
||||
pub struct Start(pub u64);
|
||||
|
||||
impl fmt::Display for Start {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "START {}", self.expr)
|
||||
write!(f, "START {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,12 +21,7 @@ pub fn start(i: &str) -> IResult<&str, Start> {
|
|||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("AT"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = take_u64(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Start {
|
||||
expr: v,
|
||||
},
|
||||
))
|
||||
Ok((i, Start(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -42,12 +35,7 @@ mod tests {
|
|||
let res = start(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
out,
|
||||
Start {
|
||||
expr: 100
|
||||
}
|
||||
);
|
||||
assert_eq!(out, Start(100));
|
||||
assert_eq!("START 100", format!("{}", out));
|
||||
}
|
||||
|
||||
|
@ -57,12 +45,7 @@ mod tests {
|
|||
let res = start(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
out,
|
||||
Start {
|
||||
expr: 100
|
||||
}
|
||||
);
|
||||
assert_eq!(out, Start(100));
|
||||
assert_eq!("START 100", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::{comment, mightbespace};
|
||||
use crate::sql::common::colons;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::statements::begin::{begin, BeginStatement};
|
||||
use crate::sql::statements::cancel::{cancel, CancelStatement};
|
||||
use crate::sql::statements::commit::{commit, CommitStatement};
|
||||
|
@ -24,8 +23,8 @@ use crate::sql::statements::remove::{remove, RemoveStatement};
|
|||
use crate::sql::statements::select::{select, SelectStatement};
|
||||
use crate::sql::statements::set::{set, SetStatement};
|
||||
use crate::sql::statements::update::{update, UpdateStatement};
|
||||
use crate::sql::statements::upsert::{upsert, UpsertStatement};
|
||||
use crate::sql::statements::yuse::{yuse, UseStatement};
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::combinator::map;
|
||||
use nom::multi::many0;
|
||||
|
@ -34,6 +33,7 @@ use nom::sequence::delimited;
|
|||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Statements(pub Vec<Statement>);
|
||||
|
@ -74,12 +74,43 @@ pub enum Statement {
|
|||
Relate(RelateStatement),
|
||||
Delete(DeleteStatement),
|
||||
Insert(InsertStatement),
|
||||
Upsert(UpsertStatement),
|
||||
Define(DefineStatement),
|
||||
Remove(RemoveStatement),
|
||||
Option(OptionStatement),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn timeout(&self) -> Option<Duration> {
|
||||
match self {
|
||||
Statement::Select(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
Statement::Create(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
Statement::Update(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
Statement::Relate(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
Statement::Delete(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
Statement::Insert(ref v) => match &v.timeout {
|
||||
Some(v) => Some(v.expr.value),
|
||||
None => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Statement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
|
@ -96,10 +127,9 @@ impl fmt::Display for Statement {
|
|||
Statement::Select(ref v) => write!(f, "{}", v),
|
||||
Statement::Create(ref v) => write!(f, "{}", v),
|
||||
Statement::Update(ref v) => write!(f, "{}", v),
|
||||
Statement::Delete(ref v) => write!(f, "{}", v),
|
||||
Statement::Relate(ref v) => write!(f, "{}", v),
|
||||
Statement::Delete(ref v) => write!(f, "{}", v),
|
||||
Statement::Insert(ref v) => write!(f, "{}", v),
|
||||
Statement::Upsert(ref v) => write!(f, "{}", v),
|
||||
Statement::Define(ref v) => write!(f, "{}", v),
|
||||
Statement::Remove(ref v) => write!(f, "{}", v),
|
||||
Statement::Option(ref v) => write!(f, "{}", v),
|
||||
|
@ -111,27 +141,29 @@ impl dbs::Process for Statement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match *self {
|
||||
Statement::Use(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Set(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Info(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Live(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Kill(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Output(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Ifelse(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Select(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Create(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Update(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Delete(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Relate(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Insert(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Upsert(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Define(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Remove(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Option(ref v) => v.process(ctx, exe, doc),
|
||||
Statement::Use(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Set(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Info(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Live(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Kill(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Begin(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Cancel(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Commit(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Output(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Ifelse(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Select(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Create(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Update(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Relate(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Delete(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Insert(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Define(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Statement::Remove(ref v) => v.process(ctx, opt, exe, doc),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -154,10 +186,9 @@ pub fn statement(i: &str) -> IResult<&str, Statement> {
|
|||
map(select, |v| Statement::Select(v)),
|
||||
map(create, |v| Statement::Create(v)),
|
||||
map(update, |v| Statement::Update(v)),
|
||||
map(delete, |v| Statement::Delete(v)),
|
||||
map(relate, |v| Statement::Relate(v)),
|
||||
map(delete, |v| Statement::Delete(v)),
|
||||
map(insert, |v| Statement::Insert(v)),
|
||||
map(upsert, |v| Statement::Upsert(v)),
|
||||
map(define, |v| Statement::Define(v)),
|
||||
map(remove, |v| Statement::Remove(v)),
|
||||
map(option, |v| Statement::Option(v)),
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -16,6 +22,18 @@ impl fmt::Display for BeginStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for BeginStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
_opt: &Options,
|
||||
_exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin(i: &str) -> IResult<&str, BeginStatement> {
|
||||
alt((begin_query, begin_basic))(i)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -16,6 +22,18 @@ impl fmt::Display for CancelStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for CancelStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
_opt: &Options,
|
||||
_exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel(i: &str) -> IResult<&str, CancelStatement> {
|
||||
alt((cancel_query, cancel_basic))(i)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -16,6 +22,18 @@ impl fmt::Display for CommitStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for CommitStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
_opt: &Options,
|
||||
_exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit(i: &str) -> IResult<&str, CommitStatement> {
|
||||
alt((commit_query, commit_basic))(i)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::data::{data, Data};
|
||||
use crate::sql::literal::{whats, Literal, Literals};
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::{whats, Value, Values};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
|
@ -18,7 +19,7 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct CreateStatement {
|
||||
pub what: Literals,
|
||||
pub what: Values,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Data>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -47,31 +48,40 @@ impl dbs::Process for CreateStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
// Loop over the select targets
|
||||
for w in self.what.to_owned() {
|
||||
match w.process(ctx, exe, doc)? {
|
||||
Literal::Table(_) => {
|
||||
i.process_table(ctx, exe);
|
||||
let mut i = Iterator::new();
|
||||
// Pass in statement config
|
||||
i.data = self.data.as_ref();
|
||||
// Ensure futures are stored
|
||||
let opt = &opt.futures(false);
|
||||
// Loop over the create targets
|
||||
for w in self.what.0.iter() {
|
||||
match w.process(ctx, opt, exe, doc)? {
|
||||
Value::Table(v) => {
|
||||
i.process_table(ctx, exe, v);
|
||||
}
|
||||
Literal::Thing(_) => {
|
||||
i.process_thing(ctx, exe);
|
||||
Value::Thing(v) => {
|
||||
i.process_thing(ctx, exe, v);
|
||||
}
|
||||
Literal::Model(_) => {
|
||||
i.process_model(ctx, exe);
|
||||
Value::Model(v) => {
|
||||
i.process_model(ctx, exe, v);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
v => {
|
||||
return Err(Error::CreateStatementError {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::algorithm::{algorithm, Algorithm};
|
||||
use crate::sql::base::{base, Base};
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::take_u64;
|
||||
use crate::sql::duration::{duration, Duration};
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::idiom::{idiom, idioms, Idiom, Idioms};
|
||||
use crate::sql::idiom;
|
||||
use crate::sql::idiom::{Idiom, Idioms};
|
||||
use crate::sql::kind::{kind, Kind};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::permission::{permissions, Permissions};
|
||||
use crate::sql::statement::{statements, Statements};
|
||||
use crate::sql::strand::strand_raw;
|
||||
use crate::sql::value::{value, values, Value, Values};
|
||||
use crate::sql::view::{view, View};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::sequence::tuple;
|
||||
use nom::{multi::many0, IResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
@ -57,10 +58,21 @@ impl dbs::Process for DefineStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
DefineStatement::Namespace(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Database(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Login(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Token(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Scope(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Table(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Event(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Field(ref v) => v.process(ctx, opt, exe, doc),
|
||||
DefineStatement::Index(ref v) => v.process(ctx, opt, exe, doc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +105,21 @@ impl fmt::Display for DefineNamespaceStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineNamespaceStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Kv)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -122,6 +149,21 @@ impl fmt::Display for DefineDatabaseStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineDatabaseStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Ns)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -163,6 +205,25 @@ impl fmt::Display for DefineLoginStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineLoginStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
match self.base {
|
||||
Base::Ns => exe.check(opt, Level::Kv)?,
|
||||
Base::Db => exe.check(opt, Level::Ns)?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn login(i: &str) -> IResult<&str, DefineLoginStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -239,6 +300,25 @@ impl fmt::Display for DefineTokenStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineTokenStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
match self.base {
|
||||
Base::Ns => exe.check(opt, Level::Kv)?,
|
||||
Base::Db => exe.check(opt, Level::Ns)?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -278,11 +358,11 @@ pub struct DefineScopeStatement {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub session: Option<Duration>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub signup: Option<Expression>,
|
||||
pub signup: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub signin: Option<Expression>,
|
||||
pub signin: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub connect: Option<Expression>,
|
||||
pub connect: Option<Value>,
|
||||
}
|
||||
|
||||
impl fmt::Display for DefineScopeStatement {
|
||||
|
@ -304,6 +384,21 @@ impl fmt::Display for DefineScopeStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineScopeStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -338,9 +433,9 @@ fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DefineScopeOption {
|
||||
Session(Duration),
|
||||
Signup(Expression),
|
||||
Signin(Expression),
|
||||
Connect(Expression),
|
||||
Signup(Value),
|
||||
Signin(Value),
|
||||
Connect(Value),
|
||||
}
|
||||
|
||||
fn scope_opts(i: &str) -> IResult<&str, DefineScopeOption> {
|
||||
|
@ -359,7 +454,7 @@ fn scope_signup(i: &str) -> IResult<&str, DefineScopeOption> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("SIGNUP")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, DefineScopeOption::Signup(v)))
|
||||
}
|
||||
|
||||
|
@ -367,7 +462,7 @@ fn scope_signin(i: &str) -> IResult<&str, DefineScopeOption> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("SIGNIN")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, DefineScopeOption::Signin(v)))
|
||||
}
|
||||
|
||||
|
@ -375,7 +470,7 @@ fn scope_connect(i: &str) -> IResult<&str, DefineScopeOption> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("CONNECT")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, DefineScopeOption::Connect(v)))
|
||||
}
|
||||
|
||||
|
@ -413,6 +508,21 @@ impl fmt::Display for DefineTableStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineTableStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn table(i: &str) -> IResult<&str, DefineTableStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -505,8 +615,8 @@ fn table_permissions(i: &str) -> IResult<&str, DefineTableOption> {
|
|||
pub struct DefineEventStatement {
|
||||
pub name: String,
|
||||
pub what: String,
|
||||
pub when: Expression,
|
||||
pub then: Statements,
|
||||
pub when: Value,
|
||||
pub then: Values,
|
||||
}
|
||||
|
||||
impl fmt::Display for DefineEventStatement {
|
||||
|
@ -519,6 +629,21 @@ impl fmt::Display for DefineEventStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineEventStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn event(i: &str) -> IResult<&str, DefineEventStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -527,16 +652,17 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> {
|
|||
let (i, name) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("WHEN")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, when) = expression(i)?;
|
||||
let (i, when) = value(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("THEN")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, then) = statements(i)?;
|
||||
let (i, then) = values(i)?;
|
||||
Ok((
|
||||
i,
|
||||
DefineEventStatement {
|
||||
|
@ -559,9 +685,9 @@ pub struct DefineFieldStatement {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub kind: Option<Kind>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub value: Option<Expression>,
|
||||
pub value: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub assert: Option<Expression>,
|
||||
pub assert: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub priority: Option<u64>,
|
||||
pub permissions: Permissions,
|
||||
|
@ -584,14 +710,30 @@ impl fmt::Display for DefineFieldStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineFieldStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("FIELD")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, name) = idiom(i)?;
|
||||
let (i, name) = idiom::local(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
let (i, opts) = many0(field_opts)(i)?;
|
||||
|
@ -630,8 +772,8 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DefineFieldOption {
|
||||
Kind(Kind),
|
||||
Value(Expression),
|
||||
Assert(Expression),
|
||||
Value(Value),
|
||||
Assert(Value),
|
||||
Priority(u64),
|
||||
Permissions(Permissions),
|
||||
}
|
||||
|
@ -652,7 +794,7 @@ fn field_value(i: &str) -> IResult<&str, DefineFieldOption> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("VALUE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, DefineFieldOption::Value(v)))
|
||||
}
|
||||
|
||||
|
@ -660,7 +802,7 @@ fn field_assert(i: &str) -> IResult<&str, DefineFieldOption> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ASSERT")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((i, DefineFieldOption::Assert(v)))
|
||||
}
|
||||
|
||||
|
@ -700,6 +842,21 @@ impl fmt::Display for DefineIndexStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for DefineIndexStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -708,12 +865,13 @@ fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
|
|||
let (i, name) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("COLUMNS")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, cols) = idioms(i)?;
|
||||
let (i, cols) = idiom::locals(i)?;
|
||||
let (i, uniq) = opt(|i| {
|
||||
shouldbespace(i)?;
|
||||
tag_no_case("UNIQUE")(i)?;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::cond::{cond, Cond};
|
||||
use crate::sql::literal::{whats, Literal, Literals};
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::{whats, Value, Values};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
|
@ -19,7 +20,7 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DeleteStatement {
|
||||
pub what: Literals,
|
||||
pub what: Values,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cond: Option<Cond>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -48,31 +49,40 @@ impl dbs::Process for DeleteStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
// Loop over the select targets
|
||||
for w in self.what.to_owned() {
|
||||
match w.process(ctx, exe, doc)? {
|
||||
Literal::Table(_) => {
|
||||
i.process_table(ctx, exe);
|
||||
let mut i = Iterator::new();
|
||||
// Pass in statement config
|
||||
i.cond = self.cond.as_ref();
|
||||
// Ensure futures are stored
|
||||
let opt = &opt.futures(false);
|
||||
// Loop over the delete targets
|
||||
for w in self.what.0.iter() {
|
||||
match w.process(ctx, opt, exe, doc)? {
|
||||
Value::Table(v) => {
|
||||
i.process_table(ctx, exe, v);
|
||||
}
|
||||
Literal::Thing(_) => {
|
||||
i.process_thing(ctx, exe);
|
||||
Value::Thing(v) => {
|
||||
i.process_thing(ctx, exe, v);
|
||||
}
|
||||
Literal::Model(_) => {
|
||||
i.process_model(ctx, exe);
|
||||
Value::Model(v) => {
|
||||
i.process_model(ctx, exe, v);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
v => {
|
||||
return Err(Error::DeleteStatementError {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::multi::separated_list0;
|
||||
|
@ -15,9 +14,9 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct IfelseStatement {
|
||||
pub exprs: Vec<(Expression, Expression)>,
|
||||
pub exprs: Vec<(Value, Value)>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub close: Option<Expression>,
|
||||
pub close: Option<Value>,
|
||||
}
|
||||
|
||||
impl fmt::Display for IfelseStatement {
|
||||
|
@ -43,18 +42,19 @@ impl dbs::Process for IfelseStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
for (ref cond, ref then) in &self.exprs {
|
||||
let v = cond.process(ctx, exe, doc)?;
|
||||
if v.as_bool() {
|
||||
return then.process(ctx, exe, doc);
|
||||
let v = cond.process(ctx, opt, exe, doc)?;
|
||||
if v.is_truthy() {
|
||||
return then.process(ctx, opt, exe, doc);
|
||||
}
|
||||
}
|
||||
match self.close {
|
||||
Some(ref v) => v.process(ctx, exe, doc),
|
||||
None => Ok(Literal::None),
|
||||
Some(ref v) => v.process(ctx, opt, exe, doc),
|
||||
None => Ok(Value::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,22 +73,22 @@ pub fn ifelse(i: &str) -> IResult<&str, IfelseStatement> {
|
|||
))
|
||||
}
|
||||
|
||||
fn exprs(i: &str) -> IResult<&str, (Expression, Expression)> {
|
||||
fn exprs(i: &str) -> IResult<&str, (Value, Value)> {
|
||||
let (i, _) = tag_no_case("IF")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, cond) = expression(i)?;
|
||||
let (i, cond) = value(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("THEN")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, then) = expression(i)?;
|
||||
let (i, then) = value(i)?;
|
||||
Ok((i, (cond, then)))
|
||||
}
|
||||
|
||||
fn close(i: &str) -> IResult<&str, Expression> {
|
||||
fn close(i: &str) -> IResult<&str, Value> {
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ELSE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, then) = expression(i)?;
|
||||
let (i, then) = value(i)?;
|
||||
Ok((i, then))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
|
@ -35,9 +36,18 @@ impl dbs::Process for InfoStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
match self {
|
||||
InfoStatement::Namespace => exe.check(opt, Level::Ns)?,
|
||||
InfoStatement::Database => exe.check(opt, Level::Db)?,
|
||||
InfoStatement::Scope(_) => exe.check(opt, Level::Db)?,
|
||||
InfoStatement::Table(_) => exe.check(opt, Level::Db)?,
|
||||
}
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::data::{single, update, values, Data};
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::table::{table, Table};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct InsertStatement {
|
||||
pub data: Expression,
|
||||
pub into: Table,
|
||||
pub data: Data,
|
||||
pub ignore: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub update: Option<Data>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub output: Option<Output>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -30,7 +34,11 @@ pub struct InsertStatement {
|
|||
|
||||
impl fmt::Display for InsertStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "INSERT {} INTO {}", self.data, self.into)?;
|
||||
write!(f, "INSERT")?;
|
||||
if self.ignore {
|
||||
write!(f, " IGNORE")?
|
||||
}
|
||||
write!(f, " INTO {} {}", self.into, self.data)?;
|
||||
if let Some(ref v) = self.output {
|
||||
write!(f, " {}", v)?
|
||||
}
|
||||
|
@ -45,23 +53,39 @@ impl dbs::Process for InsertStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
// LooParse the expression
|
||||
match self.data.process(ctx, exe, doc)? {
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
let mut i = Iterator::new();
|
||||
// Pass in statement config
|
||||
i.into = Some(&self.into);
|
||||
i.data = Some(&self.data);
|
||||
// Ensure futures are stored
|
||||
let opt = &opt.futures(false);
|
||||
// Parse the expression
|
||||
match &self.data {
|
||||
Data::ValuesExpression(_) => {
|
||||
todo!() // TODO: loop over each
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Data::SingleExpression(v) => match v.process(ctx, opt, exe, doc)? {
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
v => {
|
||||
return Err(Error::InsertStatementError {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
// Output the results
|
||||
i.output(ctx, exe)
|
||||
}
|
||||
|
@ -69,20 +93,20 @@ impl dbs::Process for InsertStatement {
|
|||
|
||||
pub fn insert(i: &str) -> IResult<&str, InsertStatement> {
|
||||
let (i, _) = tag_no_case("INSERT")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, data) = expression(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("INTO")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, into) = table(i)?;
|
||||
let (i, ignore) = opt(preceded(shouldbespace, tag_no_case("IGNORE")))(i)?;
|
||||
let (i, _) = preceded(shouldbespace, tag_no_case("INTO"))(i)?;
|
||||
let (i, into) = preceded(shouldbespace, table)(i)?;
|
||||
let (i, data) = preceded(shouldbespace, alt((values, single)))(i)?;
|
||||
let (i, update) = opt(preceded(shouldbespace, update))(i)?;
|
||||
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
InsertStatement {
|
||||
data,
|
||||
into,
|
||||
data,
|
||||
ignore: ignore.is_some(),
|
||||
update,
|
||||
output,
|
||||
timeout,
|
||||
},
|
||||
|
@ -95,11 +119,20 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn insert_statement() {
|
||||
let sql = "INSERT [1,2,3] INTO test";
|
||||
fn insert_statement_basic() {
|
||||
let sql = "INSERT INTO test (field) VALUES ($value)";
|
||||
let res = insert(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("INSERT [1, 2, 3] INTO test", format!("{}", out))
|
||||
assert_eq!("INSERT INTO test (field) VALUES ($value)", format!("{}", out))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_statement_ignore() {
|
||||
let sql = "INSERT IGNORE INTO test (field) VALUES ($value)";
|
||||
let res = insert(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("INSERT IGNORE INTO test (field) VALUES ($value)", format!("{}", out))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -25,10 +25,11 @@ impl fmt::Display for KillStatement {
|
|||
impl dbs::Process for KillStatement {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
_ctx: &Runtime,
|
||||
_opt: &Options,
|
||||
_exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::cond::{cond, Cond};
|
||||
use crate::sql::fetch::{fetch, Fetchs};
|
||||
use crate::sql::field::{fields, Fields};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::literal::{whats, Literals};
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::value::{whats, Values};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
|
@ -19,7 +19,7 @@ use std::fmt;
|
|||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LiveStatement {
|
||||
pub expr: Fields,
|
||||
pub what: Literals,
|
||||
pub what: Values,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cond: Option<Cond>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -42,10 +42,11 @@ impl fmt::Display for LiveStatement {
|
|||
impl dbs::Process for LiveStatement {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
_ctx: &Runtime,
|
||||
_opt: &Options,
|
||||
_exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,4 @@ pub mod remove;
|
|||
pub mod select;
|
||||
pub mod set;
|
||||
pub mod update;
|
||||
pub mod upsert;
|
||||
pub mod yuse;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
|
@ -35,11 +36,15 @@ impl fmt::Display for OptionStatement {
|
|||
impl dbs::Process for OptionStatement {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Return nothing
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -13,7 +12,7 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OutputStatement {
|
||||
pub what: Expression,
|
||||
pub what: Value,
|
||||
}
|
||||
|
||||
impl fmt::Display for OutputStatement {
|
||||
|
@ -26,17 +25,21 @@ impl dbs::Process for OutputStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Ensure futures are processed
|
||||
let opt = &opt.futures(true);
|
||||
// Process the output value
|
||||
self.what.process(ctx, opt, exe, doc)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output(i: &str) -> IResult<&str, OutputStatement> {
|
||||
let (i, _) = tag_no_case("RETURN")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = expression(i)?;
|
||||
let (i, v) = value(i)?;
|
||||
Ok((
|
||||
i,
|
||||
OutputStatement {
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::data::{data, Data};
|
||||
use crate::sql::literal::{whats, Literal, Literals};
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::table::{table, Table};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::{whats, Value, Values};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
@ -24,8 +24,8 @@ use std::fmt;
|
|||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RelateStatement {
|
||||
pub kind: Table,
|
||||
pub from: Literals,
|
||||
pub with: Literals,
|
||||
pub from: Values,
|
||||
pub with: Values,
|
||||
pub uniq: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Data>,
|
||||
|
@ -58,31 +58,38 @@ impl dbs::Process for RelateStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
let mut i = Iterator::new();
|
||||
// Ensure futures are stored
|
||||
let opt = &opt.futures(false);
|
||||
// Loop over the select targets
|
||||
for f in self.from.to_owned() {
|
||||
match f.process(ctx, exe, doc)? {
|
||||
Literal::Table(_) => {
|
||||
i.process_table(ctx, exe);
|
||||
for f in self.from.0.iter() {
|
||||
match f.process(ctx, opt, exe, doc)? {
|
||||
Value::Table(v) => {
|
||||
i.process_table(ctx, exe, v);
|
||||
}
|
||||
Literal::Thing(_) => {
|
||||
i.process_thing(ctx, exe);
|
||||
Value::Thing(v) => {
|
||||
i.process_thing(ctx, exe, v);
|
||||
}
|
||||
Literal::Model(_) => {
|
||||
i.process_model(ctx, exe);
|
||||
Value::Model(v) => {
|
||||
i.process_model(ctx, exe, v);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
v => {
|
||||
return Err(Error::RelateStatementError {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -95,7 +102,7 @@ pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
|||
let (i, _) = tag_no_case("RELATE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, path) = alt((relate_o, relate_i))(i)?;
|
||||
let (i, uniq) = opt(tuple((shouldbespace, tag_no_case("UNIQUE"))))(i)?;
|
||||
let (i, uniq) = opt(preceded(shouldbespace, tag_no_case("UNIQUE")))(i)?;
|
||||
let (i, data) = opt(preceded(shouldbespace, data))(i)?;
|
||||
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||
|
@ -113,7 +120,7 @@ pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
|||
))
|
||||
}
|
||||
|
||||
fn relate_o(i: &str) -> IResult<&str, (Table, Literals, Literals)> {
|
||||
fn relate_o(i: &str) -> IResult<&str, (Table, Values, Values)> {
|
||||
let (i, from) = whats(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("->")(i)?;
|
||||
|
@ -126,7 +133,7 @@ fn relate_o(i: &str) -> IResult<&str, (Table, Literals, Literals)> {
|
|||
Ok((i, (kind, from, with)))
|
||||
}
|
||||
|
||||
fn relate_i(i: &str) -> IResult<&str, (Table, Literals, Literals)> {
|
||||
fn relate_i(i: &str) -> IResult<&str, (Table, Values, Values)> {
|
||||
let (i, with) = whats(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("<-")(i)?;
|
||||
|
@ -161,4 +168,13 @@ mod tests {
|
|||
let out = res.unwrap().1;
|
||||
assert_eq!("RELATE person -> like -> animal", format!("{}", out))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relate_statement_thing() {
|
||||
let sql = "RELATE person:tobie->like->person:jaime";
|
||||
let res = relate(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("RELATE person:tobie -> like -> person:jaime", format!("{}", out))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::base::{base, Base};
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::sequence::tuple;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
@ -47,10 +49,21 @@ impl dbs::Process for RemoveStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
RemoveStatement::Namespace(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Database(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Login(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Token(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Scope(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Table(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Event(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Field(ref v) => v.process(ctx, opt, exe, doc),
|
||||
RemoveStatement::Index(ref v) => v.process(ctx, opt, exe, doc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +96,21 @@ impl fmt::Display for RemoveNamespaceStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveNamespaceStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Kv)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -112,6 +140,21 @@ impl fmt::Display for RemoveDatabaseStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveDatabaseStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Ns)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -142,6 +185,25 @@ impl fmt::Display for RemoveLoginStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveLoginStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
match self.base {
|
||||
Base::Ns => exe.check(opt, Level::Kv)?,
|
||||
Base::Db => exe.check(opt, Level::Ns)?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn login(i: &str) -> IResult<&str, RemoveLoginStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -177,6 +239,25 @@ impl fmt::Display for RemoveTokenStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveTokenStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
match self.base {
|
||||
Base::Ns => exe.check(opt, Level::Kv)?,
|
||||
Base::Db => exe.check(opt, Level::Ns)?,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -211,6 +292,21 @@ impl fmt::Display for RemoveScopeStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveScopeStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -240,6 +336,21 @@ impl fmt::Display for RemoveTableStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveTableStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -270,6 +381,21 @@ impl fmt::Display for RemoveEventStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveEventStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -278,6 +404,7 @@ fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
|
|||
let (i, name) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
Ok((
|
||||
|
@ -305,6 +432,21 @@ impl fmt::Display for RemoveFieldStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveFieldStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -313,6 +455,7 @@ fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
|
|||
let (i, name) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
Ok((
|
||||
|
@ -340,6 +483,21 @@ impl fmt::Display for RemoveIndexStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for RemoveIndexStatement {
|
||||
fn process(
|
||||
&self,
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::Db)?;
|
||||
// Continue
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
|
@ -348,6 +506,7 @@ fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
|
|||
let (i, name) = ident_raw(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("ON")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = ident_raw(i)?;
|
||||
Ok((
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::cond::{cond, Cond};
|
||||
|
@ -10,11 +11,11 @@ use crate::sql::fetch::{fetch, Fetchs};
|
|||
use crate::sql::field::{fields, Fields};
|
||||
use crate::sql::group::{group, Groups};
|
||||
use crate::sql::limit::{limit, Limit};
|
||||
use crate::sql::literal::{literals, Literal, Literals};
|
||||
use crate::sql::order::{order, Orders};
|
||||
use crate::sql::split::{split, Splits};
|
||||
use crate::sql::start::{start, Start};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::{selects, Value, Values};
|
||||
use crate::sql::version::{version, Version};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
|
@ -26,7 +27,7 @@ use std::fmt;
|
|||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SelectStatement {
|
||||
pub expr: Fields,
|
||||
pub what: Literals,
|
||||
pub what: Values,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cond: Option<Cond>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -47,6 +48,21 @@ pub struct SelectStatement {
|
|||
pub timeout: Option<Timeout>,
|
||||
}
|
||||
|
||||
impl SelectStatement {
|
||||
pub fn limit(&self) -> u64 {
|
||||
match self.limit {
|
||||
Some(Limit(v)) => v,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
pub fn start(&self) -> u64 {
|
||||
match self.start {
|
||||
Some(Start(v)) => v,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SelectStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "SELECT {} FROM {}", self.expr, self.what)?;
|
||||
|
@ -85,28 +101,46 @@ impl dbs::Process for SelectStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
let mut i = Iterator::new();
|
||||
// Pass in statement config
|
||||
i.expr = Some(&self.expr);
|
||||
i.cond = self.cond.as_ref();
|
||||
i.split = self.split.as_ref();
|
||||
i.group = self.group.as_ref();
|
||||
i.order = self.order.as_ref();
|
||||
i.limit = self.limit.as_ref();
|
||||
i.start = self.start.as_ref();
|
||||
// Ensure futures are processed
|
||||
let opt = &opt.futures(true);
|
||||
// Specify the document version
|
||||
let opt = &opt.version(self.version.as_ref());
|
||||
// Loop over the select targets
|
||||
for w in self.what.to_owned() {
|
||||
match w.process(ctx, exe, doc)? {
|
||||
Literal::Table(_) => {
|
||||
i.process_table(ctx, exe);
|
||||
for w in self.what.0.iter() {
|
||||
match w.process(ctx, opt, exe, doc)? {
|
||||
Value::Table(v) => {
|
||||
i.process_table(ctx, exe, v);
|
||||
}
|
||||
Literal::Thing(_) => {
|
||||
i.process_thing(ctx, exe);
|
||||
Value::Thing(v) => {
|
||||
i.process_thing(ctx, exe, v);
|
||||
}
|
||||
Literal::Model(_) => {
|
||||
i.process_model(ctx, exe);
|
||||
Value::Model(v) => {
|
||||
i.process_model(ctx, exe, v);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
i.process_query(ctx, exe);
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
v => {
|
||||
i.process_value(ctx, exe, v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -122,7 +156,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
|||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("FROM")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, what) = literals(i)?;
|
||||
let (i, what) = selects(i)?;
|
||||
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
||||
let (i, split) = opt(preceded(shouldbespace, split))(i)?;
|
||||
let (i, group) = opt(preceded(shouldbespace, group))(i)?;
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::param::{param, Param};
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::sequence::preceded;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SetStatement {
|
||||
pub name: Param,
|
||||
pub what: Expression,
|
||||
pub name: String,
|
||||
pub what: Value,
|
||||
}
|
||||
|
||||
impl fmt::Display for SetStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "LET {} = {}", self.name, self.what)
|
||||
write!(f, "LET ${} = {}", self.name, self.what)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,21 +30,22 @@ impl dbs::Process for SetStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
self.what.process(ctx, opt, exe, doc)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(i: &str) -> IResult<&str, SetStatement> {
|
||||
let (i, _) = tag_no_case("LET")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, n) = param(i)?;
|
||||
let (i, n) = preceded(tag("$"), ident_raw)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = tag("=")(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, w) = expression(i)?;
|
||||
let (i, w) = value(i)?;
|
||||
Ok((
|
||||
i,
|
||||
SetStatement {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::cond::{cond, Cond};
|
||||
use crate::sql::data::{data, Data};
|
||||
use crate::sql::literal::{whats, Literal, Literals};
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use crate::sql::value::{whats, Value, Values};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
|
@ -19,7 +20,7 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct UpdateStatement {
|
||||
pub what: Literals,
|
||||
pub what: Values,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Data>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -53,31 +54,41 @@ impl dbs::Process for UpdateStatement {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
exe.check(opt, Level::No)?;
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
// Loop over the select targets
|
||||
for w in self.what.to_owned() {
|
||||
match w.process(ctx, exe, doc)? {
|
||||
Literal::Table(_) => {
|
||||
i.process_table(ctx, exe);
|
||||
let mut i = Iterator::new();
|
||||
// Pass in statement config
|
||||
i.data = self.data.as_ref();
|
||||
i.cond = self.cond.as_ref();
|
||||
// Ensure futures are stored
|
||||
let opt = &opt.futures(false);
|
||||
// Loop over the update targets
|
||||
for w in self.what.0.iter() {
|
||||
match w.process(ctx, opt, exe, doc)? {
|
||||
Value::Table(v) => {
|
||||
i.process_table(ctx, exe, v);
|
||||
}
|
||||
Literal::Thing(_) => {
|
||||
i.process_thing(ctx, exe);
|
||||
Value::Thing(v) => {
|
||||
i.process_thing(ctx, exe, v);
|
||||
}
|
||||
Literal::Model(_) => {
|
||||
i.process_model(ctx, exe);
|
||||
Value::Model(v) => {
|
||||
i.process_model(ctx, exe, v);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
Value::Array(v) => {
|
||||
i.process_array(ctx, exe, v);
|
||||
}
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
Value::Object(v) => {
|
||||
i.process_object(ctx, exe, v);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
v => {
|
||||
return Err(Error::UpdateStatementError {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -115,6 +126,7 @@ mod tests {
|
|||
fn update_statement() {
|
||||
let sql = "UPDATE test";
|
||||
let res = update(sql);
|
||||
println!("{:?}", res);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("UPDATE test", format!("{}", out))
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Iterator;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::output::{output, Output};
|
||||
use crate::sql::table::{table, Table};
|
||||
use crate::sql::timeout::{timeout, Timeout};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
use nom::IResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct UpsertStatement {
|
||||
pub data: Expression,
|
||||
pub into: Table,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub output: Option<Output>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timeout: Option<Timeout>,
|
||||
}
|
||||
|
||||
impl fmt::Display for UpsertStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UPSERT {} INTO {}", self.data, self.into)?;
|
||||
if let Some(ref v) = self.output {
|
||||
write!(f, " {}", v)?
|
||||
}
|
||||
if let Some(ref v) = self.timeout {
|
||||
write!(f, " {}", v)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for UpsertStatement {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
// Create a new iterator
|
||||
let i = Iterator::new();
|
||||
// LooParse the expression
|
||||
match self.data.process(ctx, exe, doc)? {
|
||||
Literal::Object(_) => {
|
||||
i.process_object(ctx, exe);
|
||||
}
|
||||
Literal::Array(_) => {
|
||||
i.process_array(ctx, exe);
|
||||
}
|
||||
_ => {
|
||||
todo!() // Return error
|
||||
}
|
||||
};
|
||||
// Output the results
|
||||
i.output(ctx, exe)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upsert(i: &str) -> IResult<&str, UpsertStatement> {
|
||||
let (i, _) = tag_no_case("UPSERT")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, data) = expression(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("INTO")(i)?;
|
||||
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, into) = table(i)?;
|
||||
let (i, output) = opt(preceded(shouldbespace, output))(i)?;
|
||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
UpsertStatement {
|
||||
data,
|
||||
into,
|
||||
output,
|
||||
timeout,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn insert_statement() {
|
||||
let sql = "UPSERT [1,2,3] INTO test";
|
||||
let res = upsert(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!("UPSERT [1, 2, 3] INTO test", format!("{}", out))
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Auth;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::ident::ident_raw;
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::IResult;
|
||||
|
@ -36,11 +37,39 @@ impl fmt::Display for UseStatement {
|
|||
impl dbs::Process for UseStatement {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
todo!()
|
||||
_ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
if let Some(ns) = &self.ns {
|
||||
match opt.auth {
|
||||
Auth::No => exe.ns = Some(ns.to_owned()),
|
||||
Auth::Kv => exe.ns = Some(ns.to_owned()),
|
||||
Auth::Ns(v) if v == ns => exe.ns = Some(ns.to_owned()),
|
||||
_ => {
|
||||
exe.ns = None;
|
||||
return Err(Error::NsAuthenticationError {
|
||||
ns: ns.to_owned(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(db) = &self.db {
|
||||
match opt.auth {
|
||||
Auth::No => exe.db = Some(db.to_owned()),
|
||||
Auth::Kv => exe.db = Some(db.to_owned()),
|
||||
Auth::Ns(_) => exe.db = Some(db.to_owned()),
|
||||
Auth::Db(_, v) if v == db => exe.db = Some(db.to_owned()),
|
||||
_ => {
|
||||
exe.db = None;
|
||||
return Err(Error::DbAuthenticationError {
|
||||
db: db.to_owned(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use nom::IResult;
|
|||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
use std::str;
|
||||
|
||||
const SINGLE: &str = r#"'"#;
|
||||
|
@ -36,6 +37,12 @@ impl<'a> From<&'a str> for Strand {
|
|||
}
|
||||
}
|
||||
|
||||
impl Strand {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.value.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Strand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.value)
|
||||
|
@ -57,6 +64,13 @@ impl Serialize for Strand {
|
|||
}
|
||||
}
|
||||
|
||||
impl ops::Add for Strand {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
Strand::from(self.value + &other.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strand(i: &str) -> IResult<&str, Strand> {
|
||||
let (i, v) = strand_raw(i)?;
|
||||
Ok((i, Strand::from(v)))
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::literal::Literal;
|
||||
use crate::sql::statements::create::{create, CreateStatement};
|
||||
use crate::sql::statements::delete::{delete, DeleteStatement};
|
||||
use crate::sql::statements::ifelse::{ifelse, IfelseStatement};
|
||||
|
@ -13,7 +11,7 @@ use crate::sql::statements::insert::{insert, InsertStatement};
|
|||
use crate::sql::statements::relate::{relate, RelateStatement};
|
||||
use crate::sql::statements::select::{select, SelectStatement};
|
||||
use crate::sql::statements::update::{update, UpdateStatement};
|
||||
use crate::sql::statements::upsert::{upsert, UpsertStatement};
|
||||
use crate::sql::value::{value, Value};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::combinator::map;
|
||||
|
@ -24,14 +22,13 @@ use std::fmt;
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Subquery {
|
||||
Expression(Expression),
|
||||
Value(Value),
|
||||
Select(SelectStatement),
|
||||
Create(CreateStatement),
|
||||
Update(UpdateStatement),
|
||||
Delete(DeleteStatement),
|
||||
Relate(RelateStatement),
|
||||
Insert(InsertStatement),
|
||||
Upsert(UpsertStatement),
|
||||
Ifelse(IfelseStatement),
|
||||
}
|
||||
|
||||
|
@ -45,14 +42,13 @@ impl PartialOrd for Subquery {
|
|||
impl fmt::Display for Subquery {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Subquery::Expression(v) => write!(f, "({})", v),
|
||||
Subquery::Value(v) => write!(f, "({})", v),
|
||||
Subquery::Select(v) => write!(f, "({})", v),
|
||||
Subquery::Create(v) => write!(f, "({})", v),
|
||||
Subquery::Update(v) => write!(f, "({})", v),
|
||||
Subquery::Delete(v) => write!(f, "({})", v),
|
||||
Subquery::Relate(v) => write!(f, "({})", v),
|
||||
Subquery::Insert(v) => write!(f, "({})", v),
|
||||
Subquery::Upsert(v) => write!(f, "({})", v),
|
||||
Subquery::Ifelse(v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
|
@ -62,74 +58,143 @@ impl dbs::Process for Subquery {
|
|||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
Subquery::Expression(ref v) => v.process(ctx, exe, doc),
|
||||
Subquery::Ifelse(ref v) => v.process(ctx, exe, doc),
|
||||
Subquery::Value(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Subquery::Ifelse(ref v) => v.process(ctx, opt, exe, doc),
|
||||
Subquery::Select(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
let res = v.process(&ctx, &opt, exe, doc)?;
|
||||
// Process result
|
||||
match v.limit() {
|
||||
1 => match v.expr.single() {
|
||||
Some(v) => res.first(&ctx, &opt, exe).get(&ctx, &opt, exe, &v).ok(),
|
||||
None => res.first(&ctx, &opt, exe).ok(),
|
||||
},
|
||||
_ => match v.expr.single() {
|
||||
Some(v) => res.get(&ctx, &opt, exe, &v).ok(),
|
||||
None => res.ok(),
|
||||
},
|
||||
}
|
||||
}
|
||||
Subquery::Create(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
match v.process(&ctx, &opt, exe, doc)? {
|
||||
Value::Array(mut v) => match v.len() {
|
||||
1 => Ok(v.value.remove(0)),
|
||||
_ => Ok(v.into()),
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
Subquery::Update(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
match v.process(&ctx, &opt, exe, doc)? {
|
||||
Value::Array(mut v) => match v.len() {
|
||||
1 => Ok(v.value.remove(0)),
|
||||
_ => Ok(v.into()),
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
Subquery::Delete(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
match v.process(&ctx, &opt, exe, doc)? {
|
||||
Value::Array(mut v) => match v.len() {
|
||||
1 => Ok(v.value.remove(0)),
|
||||
_ => Ok(v.into()),
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
Subquery::Relate(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
match v.process(&ctx, &opt, exe, doc)? {
|
||||
Value::Array(mut v) => match v.len() {
|
||||
1 => Ok(v.value.remove(0)),
|
||||
_ => Ok(v.into()),
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
Subquery::Insert(ref v) => {
|
||||
// Duplicate options
|
||||
let opt = opt.dive()?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Add parent document
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
ctx.add_value(String::from("parent"), doc);
|
||||
}
|
||||
// Prepare context
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
// Process subquery
|
||||
match v.process(&ctx, &opt, exe, doc)? {
|
||||
Value::Array(mut v) => match v.len() {
|
||||
1 => Ok(v.value.remove(0)),
|
||||
_ => Ok(v.into()),
|
||||
},
|
||||
v => Ok(v),
|
||||
}
|
||||
Subquery::Upsert(ref v) => {
|
||||
let mut ctx = Context::new(ctx);
|
||||
if doc.is_some() {
|
||||
let doc = doc.unwrap().clone();
|
||||
ctx.add_value("parent", doc);
|
||||
}
|
||||
let ctx = ctx.freeze();
|
||||
v.process(&ctx, exe, doc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +218,7 @@ fn subquery_others(i: &str) -> IResult<&str, Subquery> {
|
|||
map(delete, |v| Subquery::Delete(v)),
|
||||
map(relate, |v| Subquery::Relate(v)),
|
||||
map(insert, |v| Subquery::Insert(v)),
|
||||
map(upsert, |v| Subquery::Upsert(v)),
|
||||
map(expression, |v| Subquery::Expression(v)),
|
||||
map(value, |v| Subquery::Value(v)),
|
||||
))(i)?;
|
||||
let (i, _) = tag(")")(i)?;
|
||||
Ok((i, v))
|
||||
|
|
|
@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Tables(Vec<Table>);
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Tables(pub Vec<Table>);
|
||||
|
||||
impl fmt::Display for Tables {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
32
src/sql/test.rs
Normal file
32
src/sql/test.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use crate::sql::expression::{expression, Expression};
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::param::{param, Param};
|
||||
use crate::sql::value::{value, Value};
|
||||
|
||||
pub trait Parse<T> {
|
||||
fn parse(val: &str) -> T;
|
||||
}
|
||||
|
||||
impl Parse<Value> for Value {
|
||||
fn parse(val: &str) -> Value {
|
||||
value(val).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<Param> for Param {
|
||||
fn parse(val: &str) -> Param {
|
||||
param(val).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<Idiom> for Idiom {
|
||||
fn parse(val: &str) -> Idiom {
|
||||
idiom(val).unwrap().1
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<Expression> for Expression {
|
||||
fn parse(val: &str) -> Expression {
|
||||
expression(val).unwrap().1
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Timeout {
|
||||
pub expr: Duration,
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
use crate::dbs;
|
||||
use crate::dbs::Executor;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::doc::Document;
|
||||
use crate::err::Error;
|
||||
use crate::sql::expression::Expression;
|
||||
use crate::sql::literal::Literal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
const NAME: &'static str = "Value";
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize)]
|
||||
pub enum Value {
|
||||
Literal(Literal),
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Value {
|
||||
Value::Literal(Literal::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Literal> for Value {
|
||||
fn from(v: Literal) -> Self {
|
||||
Value::Literal(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expression> for Value {
|
||||
fn from(v: Expression) -> Self {
|
||||
Value::Expression(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Value::Literal(ref v) => write!(f, "{}", v),
|
||||
Value::Expression(ref v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl dbs::Process for Value {
|
||||
fn process(
|
||||
&self,
|
||||
ctx: &Runtime,
|
||||
exe: &Executor,
|
||||
doc: Option<&Document>,
|
||||
) -> Result<Literal, Error> {
|
||||
match self {
|
||||
Value::Literal(ref v) => v.process(ctx, exe, doc),
|
||||
Value::Expression(ref v) => v.process(ctx, exe, doc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if s.is_human_readable() {
|
||||
match self {
|
||||
Value::Literal(ref v) => s.serialize_some(v),
|
||||
Value::Expression(ref v) => s.serialize_some(v),
|
||||
}
|
||||
} else {
|
||||
match self {
|
||||
Value::Literal(ref v) => s.serialize_newtype_variant(NAME, 0, "Literal", v),
|
||||
Value::Expression(ref v) => s.serialize_newtype_variant(NAME, 1, "Expression", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
src/sql/value/array.rs
Normal file
13
src/sql/value/array.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::array::Array;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn array(&mut self, ctx: &Runtime, opt: &Options, exe: &mut Executor, path: &Idiom) {
|
||||
let val = Value::from(Array::default());
|
||||
self.set(ctx, opt, exe, path, Value::from(val))
|
||||
}
|
||||
}
|
91
src/sql/value/decrement.rs
Normal file
91
src/sql/value/decrement.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn decrement(
|
||||
&mut self,
|
||||
ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
path: &Idiom,
|
||||
val: Value,
|
||||
) {
|
||||
match self.get(ctx, opt, exe, path) {
|
||||
Value::Number(v) => match val {
|
||||
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(v - x)),
|
||||
_ => (),
|
||||
},
|
||||
Value::Array(v) => match val {
|
||||
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(v - x)),
|
||||
x => self.set(ctx, opt, exe, path, Value::from(v - x)),
|
||||
},
|
||||
Value::None => match val {
|
||||
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(Number::from(0) - x)),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn dec_none() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("other");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 100, other: -10 }");
|
||||
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dec_number() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 90 }");
|
||||
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dec_array_number() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test[1]");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 190, 300] }");
|
||||
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dec_array_value() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 300] }");
|
||||
val.decrement(&ctx, &opt, &mut exe, &idi, Value::from(200));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dec_array_array() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [200] }");
|
||||
val.decrement(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300]"));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
180
src/sql/value/del.rs
Normal file
180
src/sql/value/del.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Process;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn del(&mut self, ctx: &Runtime, opt: &Options, exe: &mut Executor, path: &Idiom) {
|
||||
match path.parts.first() {
|
||||
// Get the current path part
|
||||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Field(p) => match path.parts.len() {
|
||||
1 => v.remove(&p.name),
|
||||
_ => match v.value.get_mut(&p.name) {
|
||||
Some(v) if v.is_some() => v.del(ctx, opt, exe, &path.next()),
|
||||
_ => (),
|
||||
},
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => match path.parts.len() {
|
||||
1 => v.value.clear(),
|
||||
_ => v.value.iter_mut().for_each(|v| v.del(ctx, opt, exe, &path.next())),
|
||||
},
|
||||
Part::First => match path.parts.len() {
|
||||
1 => {
|
||||
if v.value.len().gt(&0) {
|
||||
v.value.remove(0);
|
||||
()
|
||||
}
|
||||
}
|
||||
_ => match v.value.first_mut() {
|
||||
Some(v) => v.del(ctx, opt, exe, &path.next()),
|
||||
None => (),
|
||||
},
|
||||
},
|
||||
Part::Last => match path.parts.len() {
|
||||
1 => {
|
||||
if v.value.len().gt(&0) {
|
||||
v.value.remove(v.value.len() - 1);
|
||||
()
|
||||
}
|
||||
}
|
||||
_ => match v.value.last_mut() {
|
||||
Some(v) => v.del(ctx, opt, exe, &path.next()),
|
||||
None => (),
|
||||
},
|
||||
},
|
||||
Part::Index(i) => match path.parts.len() {
|
||||
1 => {
|
||||
if v.value.len().gt(&i.to_usize()) {
|
||||
v.value.remove(i.to_usize());
|
||||
()
|
||||
}
|
||||
}
|
||||
_ => match path.parts.len() {
|
||||
_ => match v.value.get_mut(i.to_usize()) {
|
||||
Some(v) => v.del(ctx, opt, exe, &path.next()),
|
||||
None => (),
|
||||
},
|
||||
},
|
||||
},
|
||||
Part::Where(w) => match path.parts.len() {
|
||||
1 => v.value.retain(|v| match w.process(ctx, opt, exe, Some(v)) {
|
||||
Ok(v) if v.is_truthy() => false,
|
||||
_ => true,
|
||||
}),
|
||||
_ => v.value.iter_mut().for_each(|v| {
|
||||
match w.process(ctx, opt, exe, Some(v)) {
|
||||
Ok(mut v) if v.is_truthy() => v.del(ctx, opt, exe, &path.next()),
|
||||
_ => (),
|
||||
}
|
||||
}),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
// Ignore everything else
|
||||
_ => (),
|
||||
},
|
||||
// We are done
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn del_none() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom {
|
||||
parts: vec![],
|
||||
};
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_reset() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_basic() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_wrong() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something.wrong");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_other() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.other.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_array() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
|
||||
let res = Value::parse("{ test: { something: [123, 789] } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_array_field() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1].age");
|
||||
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = Value::parse("{ test: { something: [{ age: 34 }, { }] } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn del_array_fields() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[*].age");
|
||||
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = Value::parse("{ test: { something: [{ }, { }] } }");
|
||||
val.del(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
46
src/sql/value/diff.rs
Normal file
46
src/sql/value/diff.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::operation::{Operation, Operations};
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn diff(self, _: &Runtime, _: &Options, _: &mut Executor, val: Value) -> Operations {
|
||||
let mut ops: Operations = Operations::default();
|
||||
match (self, val) {
|
||||
(Value::Object(a), Value::Object(b)) => {
|
||||
// Loop over old keys
|
||||
for (key, val) in a.value.iter() {
|
||||
if b.value.contains_key(key) == false {
|
||||
ops.0.push(Operation {
|
||||
op: String::from("remove"),
|
||||
prev: None,
|
||||
path: String::from(key),
|
||||
value: val.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// Loop over new keys
|
||||
for (key, val) in b.value.iter() {
|
||||
match a.value.contains_key(key) {
|
||||
true => ops.0.push(Operation {
|
||||
op: String::from("replace"),
|
||||
prev: None,
|
||||
path: String::from(key),
|
||||
value: val.clone(),
|
||||
}),
|
||||
false => ops.0.push(Operation {
|
||||
op: String::from("add"),
|
||||
prev: None,
|
||||
path: String::from(key),
|
||||
value: val.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
// Return operations
|
||||
ops
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
11
src/sql/value/fetch.rs
Normal file
11
src/sql/value/fetch.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn fetch(self, _ctx: &Runtime, _opt: &Options, _exe: &mut Executor, _path: &Idiom) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
12
src/sql/value/first.rs
Normal file
12
src/sql/value/first.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn first(&self, ctx: &Runtime, opt: &Options, exe: &mut Executor) -> Self {
|
||||
self.get(ctx, opt, exe, &Idiom::from(vec![Part::First]))
|
||||
}
|
||||
}
|
165
src/sql/value/get.rs
Normal file
165
src/sql/value/get.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Process;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::field::{Field, Fields};
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::statements::select::SelectStatement;
|
||||
use crate::sql::value::{Value, Values};
|
||||
|
||||
impl Value {
|
||||
pub fn get(&self, ctx: &Runtime, opt: &Options, exe: &mut Executor, path: &Idiom) -> Self {
|
||||
match path.parts.first() {
|
||||
// Get the current path part
|
||||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Field(p) => match v.value.get(&p.name) {
|
||||
Some(v) => v.get(ctx, opt, exe, &path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
_ => Value::None,
|
||||
},
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => v
|
||||
.value
|
||||
.iter()
|
||||
.map(|v| v.get(ctx, opt, exe, &path.next()))
|
||||
.collect::<Vec<Value>>()
|
||||
.into(),
|
||||
Part::First => match v.value.first() {
|
||||
Some(v) => v.get(ctx, opt, exe, &path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
Part::Last => match v.value.last() {
|
||||
Some(v) => v.get(ctx, opt, exe, &path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
Part::Index(i) => match v.value.get(i.to_usize()) {
|
||||
Some(v) => v.get(ctx, opt, exe, &path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
Part::Where(w) => v
|
||||
.value
|
||||
.iter()
|
||||
.filter_map(|v| match w.process(ctx, opt, exe, Some(v)) {
|
||||
Ok(v) if v.is_truthy() => Some(v.get(ctx, opt, exe, &path.next())),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<Value>>()
|
||||
.into(),
|
||||
_ => Value::None,
|
||||
},
|
||||
// Current path part is a thing
|
||||
Value::Thing(v) => match path.parts.len() {
|
||||
// No remote embedded fields, so just return this
|
||||
1 => Value::Thing(v.clone()),
|
||||
// Remote embedded field, so fetch the thing
|
||||
_ => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All]),
|
||||
what: Values(vec![Value::Thing(v.clone())]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
match stm.process(ctx, opt, exe, None) {
|
||||
Ok(v) => v.get(ctx, opt, exe, &path.next()),
|
||||
Err(_) => Value::None,
|
||||
}
|
||||
}
|
||||
},
|
||||
// Ignore everything else
|
||||
_ => Value::None,
|
||||
},
|
||||
// No more parts so get the value
|
||||
None => self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
use crate::sql::thing::Thing;
|
||||
|
||||
#[test]
|
||||
fn get_none() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom {
|
||||
parts: vec![],
|
||||
};
|
||||
let val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_basic() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something");
|
||||
let val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, Value::from(123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_thing() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.other");
|
||||
let val = Value::parse("{ test: { other: test:tobie, something: 123 } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(
|
||||
res,
|
||||
Value::from(Thing {
|
||||
tb: String::from("test"),
|
||||
id: String::from("tobie")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_array() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let val = Value::parse("{ test: { something: [123, 456, 789] } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, Value::from(456));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_array_thing() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(
|
||||
res,
|
||||
Value::from(Thing {
|
||||
tb: String::from("test"),
|
||||
id: String::from("jaime")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_array_field() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1].age");
|
||||
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, Value::from(36));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_array_fields() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[*].age");
|
||||
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = val.get(&ctx, &opt, &mut exe, &idi);
|
||||
assert_eq!(res, Value::from(vec![34, 36]));
|
||||
}
|
||||
}
|
92
src/sql/value/increment.rs
Normal file
92
src/sql/value/increment.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::number::Number;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn increment(
|
||||
&mut self,
|
||||
ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
path: &Idiom,
|
||||
val: Value,
|
||||
) {
|
||||
match self.get(ctx, opt, exe, path) {
|
||||
Value::Number(v) => match val {
|
||||
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(v + x)),
|
||||
_ => (),
|
||||
},
|
||||
Value::Array(v) => match val {
|
||||
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(v + x)),
|
||||
x => self.set(ctx, opt, exe, path, Value::from(v + x)),
|
||||
},
|
||||
Value::None => match val {
|
||||
Value::Number(x) => self.set(ctx, opt, exe, path, Value::from(Number::from(0) + x)),
|
||||
Value::Array(x) => self.set(ctx, opt, exe, path, Value::from(x)),
|
||||
x => self.set(ctx, opt, exe, path, Value::from(vec![x])),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn inc_none() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("other");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 100, other: +10 }");
|
||||
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inc_number() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: 100 }");
|
||||
let res = Value::parse("{ test: 110 }");
|
||||
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inc_array_number() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test[1]");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 210, 300] }");
|
||||
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(10));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inc_array_value() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 200, 300] }");
|
||||
val.increment(&ctx, &opt, &mut exe, &idi, Value::from(200));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inc_array_array() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||
let res = Value::parse("{ test: [100, 200, 300, 400, 500] }");
|
||||
val.increment(&ctx, &opt, &mut exe, &idi, Value::parse("[100, 300, 400, 500]"));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
12
src/sql/value/last.rs
Normal file
12
src/sql/value/last.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn last(&self, ctx: &Runtime, opt: &Options, exe: &mut Executor) -> Self {
|
||||
self.get(ctx, opt, exe, &Idiom::from(vec![Part::Last]))
|
||||
}
|
||||
}
|
15
src/sql/value/mod.rs
Normal file
15
src/sql/value/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
pub use self::value::*;
|
||||
|
||||
pub mod array;
|
||||
pub mod decrement;
|
||||
pub mod del;
|
||||
pub mod diff;
|
||||
pub mod fetch;
|
||||
pub mod first;
|
||||
pub mod get;
|
||||
pub mod increment;
|
||||
pub mod last;
|
||||
pub mod object;
|
||||
pub mod patch;
|
||||
pub mod set;
|
||||
pub mod value;
|
13
src/sql/value/object.rs
Normal file
13
src/sql/value/object.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::object::Object;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn object(&mut self, ctx: &Runtime, opt: &Options, exe: &mut Executor, path: &Idiom) {
|
||||
let val = Value::from(Object::default());
|
||||
self.set(ctx, opt, exe, path, val)
|
||||
}
|
||||
}
|
11
src/sql/value/patch.rs
Normal file
11
src/sql/value/patch.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::operation::Operations;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn patch(self, _: &Runtime, _: &Options, _: &mut Executor, ops: Operations) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
159
src/sql/value/set.rs
Normal file
159
src/sql/value/set.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use crate::dbs::Executor;
|
||||
use crate::dbs::Options;
|
||||
use crate::dbs::Process;
|
||||
use crate::dbs::Runtime;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::object::Object;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn set(
|
||||
&mut self,
|
||||
ctx: &Runtime,
|
||||
opt: &Options,
|
||||
exe: &mut Executor,
|
||||
path: &Idiom,
|
||||
val: Value,
|
||||
) {
|
||||
match path.parts.first() {
|
||||
// Get the current path part
|
||||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Field(p) => match v.value.get_mut(&p.name) {
|
||||
Some(v) if v.is_some() => v.set(ctx, opt, exe, &path.next(), val),
|
||||
_ => {
|
||||
let mut obj = Value::from(Object::default());
|
||||
obj.set(ctx, opt, exe, &path.next(), val);
|
||||
v.insert(&p.name, obj)
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => v
|
||||
.value
|
||||
.iter_mut()
|
||||
.for_each(|v| v.set(ctx, opt, exe, &path.next(), val.clone())),
|
||||
Part::First => match v.value.first_mut() {
|
||||
Some(v) => v.set(ctx, opt, exe, &path.next(), val),
|
||||
None => (),
|
||||
},
|
||||
Part::Last => match v.value.last_mut() {
|
||||
Some(v) => v.set(ctx, opt, exe, &path.next(), val),
|
||||
None => (),
|
||||
},
|
||||
Part::Index(i) => match v.value.get_mut(i.to_usize()) {
|
||||
Some(v) => v.set(ctx, opt, exe, &path.next(), val),
|
||||
None => (),
|
||||
},
|
||||
Part::Where(w) => {
|
||||
v.value.iter_mut().for_each(|v| match w.process(ctx, opt, exe, Some(v)) {
|
||||
Ok(mut v) if v.is_truthy() => {
|
||||
v.set(ctx, opt, exe, &path.next(), val.clone())
|
||||
}
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
// Ignore everything else
|
||||
_ => (),
|
||||
},
|
||||
// No more parts so set the value
|
||||
None => *self = val.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::dbs::test::mock;
|
||||
use crate::sql::test::Parse;
|
||||
|
||||
#[test]
|
||||
fn set_none() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom {
|
||||
parts: vec![],
|
||||
};
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("999");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_reset() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: 999 }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_basic() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 999 } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_wrong() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something.wrong");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_other() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.other.something");
|
||||
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = Value::parse("{ test: { other: { something: 999 }, something: 123 } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_array() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
|
||||
let res = Value::parse("{ test: { something: [123, 999, 789] } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(999));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_array_field() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[1].age");
|
||||
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_array_fields() {
|
||||
let (ctx, opt, mut exe) = mock();
|
||||
let idi = Idiom::parse("test.something[*].age");
|
||||
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
|
||||
val.set(&ctx, &opt, &mut exe, &idi, Value::from(21));
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
}
|
1109
src/sql/value/value.rs
Normal file
1109
src/sql/value/value.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5,14 +5,12 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Version {
|
||||
pub expr: Datetime,
|
||||
}
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct Version(pub Datetime);
|
||||
|
||||
impl fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "VERSION {}", self.expr)
|
||||
write!(f, "VERSION {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,12 +18,7 @@ pub fn version(i: &str) -> IResult<&str, Version> {
|
|||
let (i, _) = tag_no_case("VERSION")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, v) = datetime(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Version {
|
||||
expr: v,
|
||||
},
|
||||
))
|
||||
Ok((i, Version(v)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -39,12 +32,7 @@ mod tests {
|
|||
let res = version(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(
|
||||
out,
|
||||
Version {
|
||||
expr: Datetime::from("2020-01-01")
|
||||
}
|
||||
);
|
||||
assert_eq!(out, Version(Datetime::from("2020-01-01")));
|
||||
assert_eq!("VERSION \"2020-01-01T00:00:00Z\"", format!("{}", out));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use nom::IResult;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub struct View {
|
||||
pub expr: Fields,
|
||||
pub what: Tables,
|
||||
|
|
58
src/web/conf.rs
Normal file
58
src/web/conf.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use crate::dbs::Session;
|
||||
use std::net::SocketAddr;
|
||||
use warp::Filter;
|
||||
|
||||
pub fn build() -> impl Filter<Extract = (Session,), Error = warp::Rejection> + Copy {
|
||||
// Enable on any path
|
||||
let conf = warp::any();
|
||||
// Add remote ip address
|
||||
let conf = conf.and(warp::filters::addr::remote());
|
||||
// Add authorization header
|
||||
let conf = conf.and(warp::header::optional::<String>("authorization"));
|
||||
// Add http origin header
|
||||
let conf = conf.and(warp::header::optional::<String>("origin"));
|
||||
// Add session id header
|
||||
let conf = conf.and(warp::header::optional::<String>("id"));
|
||||
// Add namespace header
|
||||
let conf = conf.and(warp::header::optional::<String>("ns"));
|
||||
// Add database header
|
||||
let conf = conf.and(warp::header::optional::<String>("db"));
|
||||
// Process all headers
|
||||
conf.map(process)
|
||||
}
|
||||
|
||||
fn process(
|
||||
ip: Option<SocketAddr>,
|
||||
au: Option<String>,
|
||||
or: Option<String>,
|
||||
id: Option<String>,
|
||||
ns: Option<String>,
|
||||
db: Option<String>,
|
||||
) -> Session {
|
||||
// Specify default conf
|
||||
let mut conf = Session::default();
|
||||
// Specify client ip
|
||||
conf.ip = ip.map(|v| v.to_string()).map(|v| v.into());
|
||||
// Specify session origin
|
||||
conf.or = or.map(|v| v.into());
|
||||
// Specify session id
|
||||
conf.id = id.map(|v| v.into());
|
||||
// Specify namespace
|
||||
conf.ns = ns.map(|v| v.into());
|
||||
// Specify database
|
||||
conf.db = db.map(|v| v.into());
|
||||
// Parse authentication
|
||||
match au {
|
||||
Some(auth) if auth.starts_with("Basic") => basic(auth, conf),
|
||||
Some(auth) if auth.starts_with("Bearer") => token(auth, conf),
|
||||
_ => conf,
|
||||
}
|
||||
}
|
||||
|
||||
fn basic(auth: String, conf: Session) -> Session {
|
||||
conf
|
||||
}
|
||||
|
||||
fn token(auth: String, conf: Session) -> Session {
|
||||
conf
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use crate::dbs::Session;
|
||||
use crate::web::conf;
|
||||
use warp::http;
|
||||
use warp::Filter;
|
||||
|
||||
|
@ -7,11 +9,11 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
// Set opts method
|
||||
let opts = base.and(warp::options()).map(warp::reply);
|
||||
// Set get method
|
||||
let get = base.and(warp::get()).and_then(handler);
|
||||
let get = base.and(warp::get()).and(conf::build()).and_then(handler);
|
||||
// Specify route
|
||||
opts.or(get)
|
||||
}
|
||||
|
||||
async fn handler() -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn handler(session: Session) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
Ok(warp::reply::with_status("Ok", http::StatusCode::OK))
|
||||
}
|
||||
|
|
142
src/web/key.rs
142
src/web/key.rs
|
@ -1,4 +1,8 @@
|
|||
use crate::dbs::Session;
|
||||
use crate::sql::value::Value;
|
||||
use crate::web::conf;
|
||||
use crate::web::head;
|
||||
use bytes::Bytes;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::str;
|
||||
|
@ -12,6 +16,10 @@ pub struct Query {
|
|||
}
|
||||
|
||||
pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
// ------------------------------
|
||||
// Routes for OPTIONS
|
||||
// ------------------------------
|
||||
|
||||
let base = warp::path("key");
|
||||
// Set opts method
|
||||
let opts = base.and(warp::options()).map(warp::reply);
|
||||
|
@ -20,8 +28,12 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
// Routes for a table
|
||||
// ------------------------------
|
||||
|
||||
// All methods
|
||||
let base = warp::any();
|
||||
// Get session config
|
||||
let base = base.and(conf::build());
|
||||
// Set base path for all
|
||||
let base = path!("key" / String).and(warp::path::end());
|
||||
let base = base.and(path!("key" / String).and(warp::path::end()));
|
||||
// Set select method
|
||||
let select = base.and(warp::get()).and(warp::query()).and_then(select_all);
|
||||
// Set create method
|
||||
|
@ -39,8 +51,12 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
// Routes for a thing
|
||||
// ------------------------------
|
||||
|
||||
// All methods
|
||||
let base = warp::any();
|
||||
// Get session config
|
||||
let base = base.and(conf::build());
|
||||
// Set base path for one
|
||||
let base = path!("key" / String / String).and(warp::path::end());
|
||||
let base = base.and(path!("key" / String / String).and(warp::path::end()));
|
||||
// Set select method
|
||||
let select = base.and(warp::get()).and_then(select_one);
|
||||
// Set create method
|
||||
|
@ -78,35 +94,46 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
// Routes for a table
|
||||
// ------------------------------
|
||||
|
||||
async fn select_all(table: String, query: Query) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn select_all(
|
||||
session: Session,
|
||||
table: String,
|
||||
query: Query,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let sql = format!(
|
||||
"SELECT * FROM type::table($table) LIMIT {l} START {s}",
|
||||
l = query.limit.unwrap_or(String::from("100")),
|
||||
s = query.start.unwrap_or(String::from("0")),
|
||||
);
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
let res = crate::dbs::execute(sql.as_str(), Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
let res = crate::dbs::execute(sql.as_str(), session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
|
||||
async fn create_all(
|
||||
session: Session,
|
||||
table: String,
|
||||
body: bytes::Bytes,
|
||||
body: Bytes,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let data = str::from_utf8(&body).unwrap();
|
||||
match crate::sql::value::json(data) {
|
||||
Ok((_, data)) => {
|
||||
let sql = "CREATE type::table($table) CONTENT $data";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("data", str::from_utf8(&body).unwrap().to_owned());
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("data"), Value::from(data));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_all(table: String) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn delete_all(session: Session, table: String) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let sql = "DELETE type::table($table)";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
|
||||
|
@ -114,62 +141,91 @@ async fn delete_all(table: String) -> Result<impl warp::Reply, warp::Rejection>
|
|||
// Routes for a thing
|
||||
// ------------------------------
|
||||
|
||||
async fn select_one(table: String, id: String) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn select_one(
|
||||
session: Session,
|
||||
table: String,
|
||||
id: String,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let sql = "SELECT * FROM type::thing($table, $id)";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("id", id);
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("id"), Value::from(id));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
|
||||
async fn create_one(
|
||||
session: Session,
|
||||
table: String,
|
||||
id: String,
|
||||
body: bytes::Bytes,
|
||||
body: Bytes,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let data = str::from_utf8(&body).unwrap();
|
||||
match crate::sql::value::json(data) {
|
||||
Ok((_, data)) => {
|
||||
let sql = "CREATE type::thing($table, $id) CONTENT $data";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("id", id);
|
||||
var.insert("data", str::from_utf8(&body).unwrap().to_owned());
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("id"), Value::from(id));
|
||||
vars.insert(String::from("data"), Value::from(data));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_one(
|
||||
session: Session,
|
||||
table: String,
|
||||
id: String,
|
||||
body: bytes::Bytes,
|
||||
body: Bytes,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let data = str::from_utf8(&body).unwrap();
|
||||
match crate::sql::value::json(data) {
|
||||
Ok((_, data)) => {
|
||||
let sql = "UPDATE type::thing($table, $id) CONTENT $data";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("id", id);
|
||||
var.insert("data", str::from_utf8(&body).unwrap().to_owned());
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("id"), Value::from(id));
|
||||
vars.insert(String::from("data"), Value::from(data));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn modify_one(
|
||||
session: Session,
|
||||
table: String,
|
||||
id: String,
|
||||
body: bytes::Bytes,
|
||||
body: Bytes,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let data = str::from_utf8(&body).unwrap();
|
||||
match crate::sql::value::json(data) {
|
||||
Ok((_, data)) => {
|
||||
let sql = "UPDATE type::thing($table, $id) MERGE $data";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("id", id);
|
||||
var.insert("data", str::from_utf8(&body).unwrap().to_owned());
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("id"), Value::from(id));
|
||||
vars.insert(String::from("data"), Value::from(data));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_one(table: String, id: String) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn delete_one(
|
||||
session: Session,
|
||||
table: String,
|
||||
id: String,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let sql = "DELETE type::thing($table, $id)";
|
||||
let mut var = HashMap::new();
|
||||
var.insert("table", table);
|
||||
var.insert("id", id);
|
||||
let res = crate::dbs::execute(sql, Some(var)).unwrap();
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert(String::from("table"), Value::from(table));
|
||||
vars.insert(String::from("id"), Value::from(id));
|
||||
let res = crate::dbs::execute(sql, session, Some(vars)).await.unwrap();
|
||||
Ok(warp::reply::json(&res))
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
mod conf;
|
||||
mod export;
|
||||
mod head;
|
||||
mod import;
|
||||
|
@ -13,18 +14,18 @@ mod version;
|
|||
|
||||
use anyhow::Error;
|
||||
use std::net::SocketAddr;
|
||||
use warp::Filter;
|
||||
use uuid::Uuid;
|
||||
use warp::Filter;
|
||||
|
||||
const ID: &'static str = "Request-Id";
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn init(opts: &clap::ArgMatches) -> Result<(), Error> {
|
||||
pub async fn init(conf: &clap::ArgMatches) -> Result<(), Error> {
|
||||
//
|
||||
let adr = opts.value_of("bind").unwrap();
|
||||
let adr = conf.value_of("bind").unwrap();
|
||||
//
|
||||
let adr: SocketAddr = adr.parse().expect("Unable to parse socket address");
|
||||
|
||||
//
|
||||
let web = root::config()
|
||||
// Version endpoint
|
||||
.or(version::config())
|
||||
|
@ -40,9 +41,9 @@ pub async fn init(opts: &clap::ArgMatches) -> Result<(), Error> {
|
|||
.or(import::config())
|
||||
// Backup endpoint
|
||||
.or(sync::config())
|
||||
// Endpoints for sql queries
|
||||
// SQL query endpoint
|
||||
.or(sql::config())
|
||||
// Key for key queries
|
||||
// API query endpoint
|
||||
.or(key::config())
|
||||
// End routes setup
|
||||
;
|
||||
|
@ -60,7 +61,6 @@ pub async fn init(opts: &clap::ArgMatches) -> Result<(), Error> {
|
|||
|
||||
let web = web.with(log::write());
|
||||
|
||||
|
||||
info!("Starting web server on {}", adr);
|
||||
|
||||
warp::serve(web).run(adr).await;
|
||||
|
|
|
@ -1,45 +1,8 @@
|
|||
use warp::http::Uri;
|
||||
use warp::Filter;
|
||||
|
||||
pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
warp::path::end().and(warp::fs::dir("app"))
|
||||
// warp::path::end().and(warp::get()).map(warp::reply)
|
||||
warp::path::end()
|
||||
.and(warp::get())
|
||||
.map(|| warp::redirect(Uri::from_static("https://app.surrealdb.com")))
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
https://github.com/pyros2097/rust-embed
|
||||
|
||||
use rust_embed::RustEmbed;
|
||||
use warp::{http::header::HeaderValue, path::Tail, reply::Response, Filter, Rejection, Reply};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "examples/public/"]
|
||||
struct Asset;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let index_html = warp::path::end().and_then(serve_index);
|
||||
let dist = warp::path("dist").and(warp::path::tail()).and_then(serve);
|
||||
|
||||
let routes = index_html.or(dist);
|
||||
warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
|
||||
}
|
||||
|
||||
async fn serve_index() -> Result<impl Reply, Rejection> {
|
||||
serve_impl("index.html")
|
||||
}
|
||||
|
||||
async fn serve(path: Tail) -> Result<impl Reply, Rejection> {
|
||||
serve_impl(path.as_str())
|
||||
}
|
||||
|
||||
fn serve_impl(path: &str) -> Result<impl Reply, Rejection> {
|
||||
let asset = Asset::get(path).ok_or_else(warp::reject::not_found)?;
|
||||
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||
|
||||
let mut res = Response::new(asset.into());
|
||||
res.headers_mut().insert("content-type", HeaderValue::from_str(mime.as_ref()).unwrap());
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use crate::dbs::Session;
|
||||
use crate::web::conf;
|
||||
use crate::web::head;
|
||||
use bytes::Bytes;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use warp::Filter;
|
||||
|
||||
|
@ -10,6 +13,7 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
// Set post method
|
||||
let post = base
|
||||
.and(warp::post())
|
||||
.and(conf::build())
|
||||
.and(warp::header::<String>(http::header::CONTENT_TYPE.as_str()))
|
||||
.and(warp::body::content_length_limit(1024 * 1024 * 1)) // 1MiB
|
||||
.and(warp::body::bytes())
|
||||
|
@ -30,10 +34,14 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
opts.or(post).or(sock).with(head::cors())
|
||||
}
|
||||
|
||||
async fn handler(out: String, sql: bytes::Bytes) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
async fn handler(
|
||||
session: Session,
|
||||
output: String,
|
||||
sql: Bytes,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let sql = std::str::from_utf8(&sql).unwrap();
|
||||
let res = crate::dbs::execute(sql, None).unwrap();
|
||||
match out.as_ref() {
|
||||
let res = crate::dbs::execute(sql, session, None).await.unwrap();
|
||||
match output.as_ref() {
|
||||
"application/json" => Ok(warp::reply::json(&res)),
|
||||
"application/cbor" => Ok(warp::reply::json(&res)),
|
||||
_ => Err(warp::reject::not_found()),
|
||||
|
|
Loading…
Reference in a new issue