Large update to code to convert Golang to Rust

This commit is contained in:
Tobie Morgan Hitchcock 2022-01-13 17:36:41 +00:00
parent 4f4793975e
commit 6e031110bb
94 changed files with 7039 additions and 2688 deletions

1661
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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
View file

@ -0,0 +1,2 @@
// Specifies how many subqueries will be processed recursively before the query fails.
pub const MAX_RECURSIVE_QUERIES: usize = 16;

View file

@ -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;

View file

@ -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>;
}

View file

@ -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
View 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)
}

View file

@ -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
View 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
}};
}

View file

@ -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;

View file

@ -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)]

View file

@ -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, ()))
}

View file

@ -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 {

View file

@ -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));
}
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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";

View file

@ -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")
},
])
);

View file

@ -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)?;

View file

@ -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")));
}
}

View file

@ -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
View 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
View 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));
}
}

View file

@ -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")
},
])
);

View file

@ -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,
}

View file

@ -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")
],
}
);

View file

@ -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)?;

View file

@ -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));
}
}

View file

@ -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());
}
}

View file

@ -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;

View file

@ -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)
}
}

View file

@ -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
View 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,
}

View file

@ -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))
}

View file

@ -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,

View file

@ -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"));
}
}

View file

@ -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
View 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"))));
}
}

View file

@ -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,
}
);

View file

@ -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));
}
}

View file

@ -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
View 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"#));
}
}

View file

@ -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")
},
])
);

View file

@ -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));
}
}

View file

@ -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)),

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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,
})
}
};
}

View file

@ -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)?;

View file

@ -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,
})
}
};
}

View file

@ -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))
}

View file

@ -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!()
}
}

View file

@ -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))
}
}

View file

@ -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!()
}
}

View file

@ -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!()
}
}

View file

@ -16,5 +16,4 @@ pub mod remove;
pub mod select;
pub mod set;
pub mod update;
pub mod upsert;
pub mod yuse;

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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))
}
}

View file

@ -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((

View file

@ -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)?;

View file

@ -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 {

View file

@ -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))

View file

@ -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))
}
}

View file

@ -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)
}
}

View file

@ -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)))

View file

@ -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))

View file

@ -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
View 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
}
}

View file

@ -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,
}

View file

@ -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
View 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))
}
}

View 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
View 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
View 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
View 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
View 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
View 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]));
}
}

View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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));
}
}

View file

@ -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
View 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
}

View file

@ -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))
}

View file

@ -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))
}

View file

@ -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;

View file

@ -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)
}
*/

View file

@ -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()),