Continue with initial code

This commit is contained in:
Tobie Morgan Hitchcock 2021-03-29 16:43:37 +01:00
parent ab0014ad02
commit 5d57c105b9
109 changed files with 4224 additions and 1814 deletions

1289
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,56 +5,57 @@ edition = "2018"
version = "0.0.0"
authors = ["Tobie Morgan Hitchcock <tobie@surrealdb.com>"]
[dependencies]
clap = "2.33.1"
failure = "0.1.8"
futures = "0.3.5"
log = "0.4.8"
maplit = "1.0.2"
nom = "5.1.2"
regex = "1.3.9"
http = "0.2"
bytes = "0.5.5"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies.ctx]
version = "0.2.0"
package = "io-context"
[dependencies]
anyhow = "1.0.40"
bytes = "1.0.1"
clap = "2.33.3"
futures = "0.3.13"
http = "0.2.3"
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"
xid = "1.0.0"
[dependencies.dec]
version = "1.6.0"
version = "1.10.3"
package = "rust_decimal"
features = ["serde-float"]
[dependencies.fern]
version = "0.6.0"
features = ["colored"]
[dependencies.uuid]
version = "0.8.1"
version = "0.8.2"
features = ["serde", "v4"]
[dependencies.warp]
version = "0.2.3"
version = "0.3.1"
features = ["compression", "websocket"]
[dependencies.tokio]
version = "0.2"
version = "1.4.0"
features = ["macros"]
[dependencies.reqwest]
version = "0.10.6"
version = "0.11.2"
features = ["blocking"]
[dependencies.chrono]
version = "0.4.11"
version = "0.4.19"
features = ["serde"]
[dependencies.serde]
version = "1.0.111"
version = "1.0.125"
features = ["derive"]
[dependencies.serde_cbor]
version = "0.11.1"
[dependencies.serde_json]
version = "1.0.55"
version = "1.0.64"

1
app/index.html Normal file
View file

@ -0,0 +1 @@
<meta http-equiv="refresh" content="0;url=https://app.surrealdb.com/" />

View file

@ -1,7 +1,7 @@
use reqwest::header::CONTENT_TYPE;
use anyhow::Error;
use reqwest::blocking::Body;
use reqwest::blocking::Client;
use failure::Error;
use reqwest::header::CONTENT_TYPE;
use std::fs::OpenOptions;
use std::io::copy;
@ -38,11 +38,7 @@ pub fn init(matches: &clap::ArgMatches) -> Result<(), Error> {
fn backup_file_to_file(_: &clap::ArgMatches, from: &str, into: &str) -> Result<(), Error> {
let mut from = OpenOptions::new().read(true).open(from)?;
let mut into = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(into)?;
let mut into = OpenOptions::new().write(true).create(true).truncate(true).open(into)?;
copy(&mut from, &mut into)?;
@ -63,11 +59,7 @@ fn backup_http_to_file(matches: &clap::ArgMatches, from: &str, into: &str) -> Re
.send()?
.error_for_status()?;
let mut into = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(into)?;
let mut into = OpenOptions::new().write(true).create(true).truncate(true).open(into)?;
copy(&mut from, &mut into)?;

View file

@ -1,6 +1,6 @@
use reqwest::header::CONTENT_TYPE;
use anyhow::Error;
use reqwest::blocking::Client;
use failure::Error;
use reqwest::header::CONTENT_TYPE;
use std::fs::OpenOptions;
use std::io::copy;
@ -15,11 +15,7 @@ pub fn init(matches: &clap::ArgMatches) -> Result<(), Error> {
// and if there is a problem opening
// the file, then return an error.
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file)?;
let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(file)?;
// Parse all other cli arguments

View file

@ -1,6 +1,6 @@
use reqwest::header::CONTENT_TYPE;
use anyhow::Error;
use reqwest::blocking::Client;
use failure::Error;
use reqwest::header::CONTENT_TYPE;
use std::fs::OpenOptions;
use std::io::prelude::*;

View file

@ -1,7 +1,7 @@
extern crate std;
extern crate log;
extern crate fern;
extern crate chrono;
extern crate fern;
extern crate log;
extern crate std;
use self::fern::colors::Color;
use self::fern::colors::ColoredLevelConfig;

View file

@ -1,7 +1,7 @@
use crate::kvs;
use crate::web;
use anyhow::Error;
use clap;
use failure::Error;
const LOGO: &'static str = "
.d8888b. 888 8888888b. 888888b.

View file

@ -1,5 +1,5 @@
use anyhow::Error;
use clap;
use failure::Error;
const NAME: &'static str = env!("CARGO_PKG_NAME");
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
@ -15,13 +15,7 @@ pub fn init(_: &clap::ArgMatches) -> Result<(), Error> {
get_cfg!(target_arch: "x86", "x86_64", "mips", "powerpc", "powerpc64", "arm", "aarch64");
println!(
"{} {} for {} on {}",
NAME,
VERSION,
target_os(),
target_arch()
);
println!("{} {} for {} on {}", NAME, VERSION, target_os(), target_arch());
Ok(())
}

20
src/ctx/canceller.rs Normal file
View file

@ -0,0 +1,20 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
pub struct Canceller {
/// A reference to the canceled value of a context.
cancelled: Arc<AtomicBool>,
}
impl Canceller {
// Create a new Canceller
pub fn new(cancelled: Arc<AtomicBool>) -> Canceller {
Canceller {
cancelled,
}
}
// Cancel the context.
pub fn cancel(self) {
self.cancelled.store(true, Ordering::SeqCst);
}
}

177
src/ctx/context.rs Normal file
View file

@ -0,0 +1,177 @@
use crate::ctx::canceller::Canceller;
use crate::ctx::reason::Reason;
use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
pub type Parent = Arc<Context>;
pub struct Context {
// An optional parent context.
parent: Option<Arc<Context>>,
// An optional deadline.
deadline: Option<Instant>,
// Wether or not this context is cancelled.
cancelled: Arc<AtomicBool>,
// A collection of read only values stored in this context.
values: Option<HashMap<&'static str, Box<dyn Any + Send + Sync>>>,
}
impl Context {
// Create an empty background context.
pub fn background() -> Context {
Context {
values: None,
parent: None,
deadline: None,
cancelled: Arc::new(AtomicBool::new(false)),
}
}
// Create a new child from a frozen context.
pub fn new(parent: &Arc<Context>) -> Context {
Context {
values: None,
parent: Some(Arc::clone(parent)),
deadline: parent.deadline,
cancelled: Arc::new(AtomicBool::new(false)),
}
}
// Freeze the context so it can be used to create child contexts. The
// parent context can no longer be modified once it has been frozen.
pub fn freeze(mut self) -> Arc<Context> {
if let Some(ref mut values) = self.values {
values.shrink_to_fit();
}
Arc::new(self)
}
// Add cancelation to the context. The value that is returned will cancel
// the context and it's children once called.
pub fn add_cancel(&mut self) -> Canceller {
let cancelled = self.cancelled.clone();
Canceller::new(cancelled)
}
// Add a deadline to the context. If the current deadline is sooner than
// the provided deadline, this method does nothing.
pub fn add_deadline(&mut self, deadline: Instant) {
match self.deadline {
Some(current) if current < deadline => (),
_ => self.deadline = Some(deadline),
}
}
// Add a timeout to the context. If the current timeout is sooner than
// the provided timeout, this method does nothing.
pub fn add_timeout(&mut self, timeout: Duration) {
self.add_deadline(Instant::now() + timeout)
}
// Add a value to the context. It overwrites any previously set values
// with the same key.
pub fn add_value<V>(&mut self, key: &'static str, value: V)
where
V: Any + Send + Sync + Sized,
{
if let Some(ref mut values) = self.values {
values.insert(key, Box::new(value));
} else {
self.values = Some(HashMap::new());
self.add_value(key, value);
}
}
// Get the deadline for this operation, if any. This is useful for
// checking if a long job should be started or not.
pub fn deadline(&self) -> Option<Instant> {
self.deadline
}
// Check if the context is done. If it returns `None` the operation may
// proceed, otherwise the operation should be stopped.
pub fn done(&self) -> Option<Reason> {
match self.deadline {
Some(deadline) if deadline <= Instant::now() => Some(Reason::Timedout),
// TODO: see if we can relax the ordering.
_ if self.cancelled.load(Ordering::SeqCst) => Some(Reason::Canceled),
_ => match self.parent {
Some(ref parent_ctx) => parent_ctx.done(),
_ => None,
},
}
}
// Check if the context is ok to continue.
pub fn is_ok(&self) -> bool {
self.done().is_none()
}
// Check if the context is not ok to continue.
pub fn is_err(&self) -> bool {
self.done().is_some()
}
// Check if the context is not ok to continue.
pub fn is_done(&self) -> bool {
self.done().is_some()
}
// Check if the context is not ok to continue, because it timed out.
pub fn is_timedout(&self) -> bool {
match self.done() {
Some(Reason::Timedout) => true,
_ => false,
}
}
// Check if the context is not ok to continue, because it was cancelled.
pub fn is_cancelled(&self) -> bool {
match self.done() {
Some(Reason::Canceled) => true,
_ => false,
}
}
// Check if the status of the context. This will return a Result, with an Ok
// if the operation may proceed, and an Error if it should be stopped.
pub fn check(&self) -> Result<(), Reason> {
match self.done() {
Some(reason) => Err(reason),
None => Ok(()),
}
}
// Get a value from the context. If no value is stored under the
// provided key, then this will return None.
pub fn value<V>(&self, key: &'static str) -> Option<&V>
where
V: Any + Send + Sync + Sized,
{
if let Some(ref values) = self.values {
if let Some(value) = values.get(&key) {
let value: &dyn Any = &**value;
return value.downcast_ref::<V>();
}
}
match self.parent {
Some(ref parent) => parent.value(key),
_ => None,
}
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Context")
.field("parent", &self.parent)
.field("deadline", &self.deadline)
.field("cancelled", &self.cancelled)
.field("values", &self.values.as_ref().map(|_| "values"))
.finish()
}
}

16
src/ctx/mod.rs Normal file
View file

@ -0,0 +1,16 @@
// Copyright 2017 Thomas de Zeeuw
//
// https://docs.rs/io-context/0.2.0/io_context/
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
// or http://opensource.org/licenses/MIT>, at your option. This file may not be
// used, copied, modified, or distributed except according to those terms.
pub use self::canceller::*;
pub use self::context::*;
pub use self::reason::*;
pub mod canceller;
pub mod context;
pub mod reason;

30
src/ctx/reason.rs Normal file
View file

@ -0,0 +1,30 @@
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Reason {
Timedout,
Canceled,
}
impl Error for Reason {}
impl fmt::Display for Reason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Reason::Timedout => write!(f, "Context timedout"),
Reason::Canceled => write!(f, "Context canceled"),
}
}
}
impl From<Reason> for io::Error {
fn from(reason: Reason) -> Self {
let kind = match reason {
Reason::Timedout => io::ErrorKind::TimedOut,
Reason::Canceled => io::ErrorKind::Other,
};
io::Error::new(kind, reason.to_string())
}
}

View file

@ -1,29 +1,29 @@
use crate::dbs::exe::Executor;
use crate::dbs::res::Responses;
use crate::ctx::Context;
use crate::dbs::executor::Executor;
use crate::dbs::response::Responses;
use crate::err::Error;
use crate::sql;
use crate::sql::query::Query;
use ctx::Context;
use std::collections::HashMap;
type Vars<'a> = Option<HashMap<&'a str, String>>;
pub type Vars<'a> = Option<HashMap<&'a str, String>>;
pub fn execute(txt: &str, vars: Vars) -> Result<Responses, Error> {
// Parse the SQL query into an AST
let ast = sql::parse(txt)?;
// Create a new execution context
let ctx = Context::background().freeze();
// Create a new query executor
let exe = Executor::new();
// Process all of the queries
exe.execute(ast)
exe.execute(&ctx, ast)
}
pub fn process(ast: Query, vars: Vars) -> Result<Responses, Error> {
// Create a new execution context
// let ctx = None;
// ctx.set("server.ip");
// ctx.set("client.ip");
let ctx = Context::background().freeze();
// Create a new query executor
let exe = Executor::new();
// Process all of the queries
exe.execute(ast)
exe.execute(&ctx, ast)
}

View file

@ -1,42 +0,0 @@
use crate::dbs::res::{Response, Responses};
use crate::err::Error;
use crate::sql::query::Query;
use crate::sql::statement::Statement;
use ctx::Context;
use std::fs;
use std::time::Instant;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct Executor {
id: String,
ns: String,
db: String,
}
impl Executor {
pub fn new() -> Executor {
Executor {
id: String::from("id"),
ns: String::from("ns"),
db: String::from("db"),
}
}
pub fn execute(&self, qry: Query) -> Result<Responses, Error> {
let mut r: Vec<Response> = vec![];
for stm in qry.statements().iter() {
let now = Instant::now();
let res = stm.execute();
let dur = now.elapsed();
r.push(Response {
sql: format!("{}", stm),
time: format!("{:?}", dur),
status: String::from(""),
detail: String::from(""),
result: Some(res),
});
}
Ok(Responses(r))
}
}

50
src/dbs/executor.rs Normal file
View file

@ -0,0 +1,50 @@
use crate::ctx::Parent;
use crate::dbs::response::{Response, Responses};
use crate::dbs::Process;
use crate::err::Error;
use crate::sql::query::Query;
use std::time::Instant;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Executor {
pub id: String,
pub ns: String,
pub db: String,
}
impl Executor {
pub fn new() -> Executor {
Executor {
id: String::from("id"),
ns: String::from("ns"),
db: String::from("db"),
}
}
pub fn execute(&self, ctx: &Parent, qry: Query) -> Result<Responses, Error> {
let mut r: Vec<Response> = vec![];
for stm in qry.statements().iter() {
// Get the statement start time
let now = Instant::now();
// Process a single statement
let res = stm.process(&ctx, self, None);
// Get the statement end time
let dur = now.elapsed();
r.push(Response {
sql: format!("{}", stm),
time: format!("{:?}", dur),
status: match res {
Ok(_) => String::from("OK"),
Err(_) => String::from("ERR"),
},
result: match res {
Ok(v) => Some(v),
Err(_) => None,
},
})
}
Ok(Responses(r))
}
}

21
src/dbs/iterator.rs Normal file
View file

@ -0,0 +1,21 @@
use crate::ctx::Parent;
use crate::dbs::Executor;
use crate::err::Error;
use crate::sql::literal::Literal;
pub struct Iterator {}
impl Iterator {
pub fn new() -> Iterator {
Iterator {}
}
pub fn process_query(&self, ctx: &Parent, exe: &Executor) {}
pub fn process_table(&self, ctx: &Parent, exe: &Executor) {}
pub fn process_thing(&self, ctx: &Parent, exe: &Executor) {}
pub fn process_model(&self, ctx: &Parent, exe: &Executor) {}
pub fn process_array(&self, ctx: &Parent, exe: &Executor) {}
pub fn process_object(&self, ctx: &Parent, exe: &Executor) {}
pub fn output(&self, ctx: &Parent, exe: &Executor) -> Result<Literal, Error> {
Ok(Literal::Null)
}
}

View file

@ -1,5 +1,10 @@
mod dbs;
mod exe;
mod res;
mod executor;
mod iterator;
mod process;
mod response;
pub use self::dbs::*;
pub use self::executor::*;
pub use self::iterator::*;
pub use self::process::*;

14
src/dbs/process.rs Normal file
View file

@ -0,0 +1,14 @@
use crate::ctx::Parent;
use crate::dbs::executor::Executor;
use crate::doc::Document;
use crate::err::Error;
use crate::sql::literal::Literal;
pub trait Process {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error>;
}

View file

@ -1,17 +0,0 @@
use crate::sql::statement::Statement;
use serde::{Deserialize, Serialize};
// pub type Output = std::result::Result<warp::reply::Json, ErrorResponse>;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Responses(pub Vec<Response>);
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Response {
pub sql: String,
pub time: String,
pub status: String,
pub detail: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<String>,
}

15
src/dbs/response.rs Normal file
View file

@ -0,0 +1,15 @@
use crate::sql::literal::Literal;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
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<String>,
pub result: Option<Literal>,
}

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,2 @@
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Document {}

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -1,67 +1,68 @@
use crate::sql::duration::Duration;
use crate::sql::thing::Thing;
use serde::{Deserialize, Serialize};
use serde_cbor::error::Error as CborError;
use serde_json::Error as JsonError;
use serde_json::error::Error as JsonError;
use thiserror::Error;
#[derive(Fail, Debug)]
#[derive(Error, Debug)]
pub enum Error {
#[fail(display = "Specify a namespace to use")]
NSError,
#[error("Specify a namespace to use")]
NsError,
#[fail(display = "Specify a database to use")]
DBError,
#[error("Specify a database to use")]
DbError,
#[fail(display = "Specify some SQL code to execute")]
#[error("Specify some SQL code to execute")]
EmptyError,
#[fail(display = "Parse error at position {} when parsing '{}'", pos, sql)]
ParseError { pos: usize, sql: String },
#[error("Parse error at position {pos} when parsing '{sql}'")]
ParseError {
pos: usize,
sql: String,
},
#[fail(display = "Query timeout of {} exceeded", timer)]
TimerError { timer: Duration },
#[error("Wrong number of arguments at position {pos} when parsing '{sql}'")]
CountError {
pos: usize,
sql: String,
},
#[fail(display = "Database record `{}` already exists", thing)]
ExistError { thing: Thing },
#[error("Query timeout of {timer} exceeded")]
TimerError {
timer: Duration,
},
#[fail(display = "Database index `{}` already contains `{}`", index, thing)]
IndexError { index: String, thing: Thing },
#[error("Database record `{thing}` already exists")]
ExistError {
thing: Thing,
},
#[fail(display = "You don't have permission to perform the query `{}`", query)]
PermsError { query: String },
#[error("Database index `{index}` already contains `{thing}`")]
IndexError {
index: String,
thing: Thing,
},
#[fail(
display = "Unable to write to the `{}` table while setup as a view",
table
)]
WriteError { table: String },
#[error("You don't have permission to perform the query `{query}`")]
PermsError {
query: String,
},
#[fail(
display = "You don't have permission to perform this query on the `{}` table",
table
)]
TableError { table: String },
#[error("Unable to write to the `{table}` table while setup as a view")]
WriteError {
table: String,
},
#[fail(display = "JSON Error: {}", _0)]
JsonError(#[cause] JsonError),
#[error("You don't have permission to perform this query on the `{table}` table")]
TableError {
table: String,
},
#[fail(display = "CBOR Error: {}", _0)]
CborError(#[cause] CborError),
}
#[error("JSON Error: {0}")]
JsonError(JsonError),
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub code: usize,
pub info: String,
}
impl Error {
pub fn build(&self) -> Message {
Message {
code: 400,
info: format!("{}", self),
}
}
#[error("CBOR Error: {0}")]
CborError(CborError),
}
impl From<JsonError> for Error {

48
src/fnc/cast.rs Normal file
View file

@ -0,0 +1,48 @@
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::literal::Literal;
pub fn run(ctx: &Context, name: &String, val: Literal) -> Result<Literal, Error> {
match name.as_str() {
"bool" => bool(ctx, val),
"int" => int(ctx, val),
"float" => float(ctx, val),
"string" => string(ctx, val),
"number" => number(ctx, val),
"decimal" => number(ctx, val),
"datetime" => datetime(ctx, val),
"duration" => duration(ctx, val),
_ => Ok(val),
}
}
pub fn bool(ctx: &Context, val: Literal) -> Result<Literal, Error> {
match val.as_bool() {
true => Ok(Literal::True),
false => Ok(Literal::False),
}
}
pub fn int(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Int(val.as_int()))
}
pub fn float(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Float(val.as_float()))
}
pub fn string(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Strand(val.as_strand()))
}
pub fn number(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Number(val.as_number()))
}
pub fn datetime(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Datetime(val.as_datetime()))
}
pub fn duration(ctx: &Context, val: Literal) -> Result<Literal, Error> {
Ok(Literal::Duration(val.as_duration()))
}

7
src/fnc/future.rs Normal file
View file

@ -0,0 +1,7 @@
use crate::ctx::Parent;
use crate::err::Error;
use crate::sql::literal::Literal;
pub fn run(ctx: &Parent, args: Literal) -> Result<Literal, Error> {
todo!()
}

11
src/fnc/mod.rs Normal file
View file

@ -0,0 +1,11 @@
use crate::ctx::Parent;
use crate::err::Error;
use crate::sql::literal::Literal;
pub mod cast;
pub mod future;
pub mod operate;
pub fn run(ctx: &Parent, name: &String, args: Vec<Literal>) -> Result<Literal, Error> {
todo!()
}

435
src/fnc/operate.rs Normal file
View file

@ -0,0 +1,435 @@
use crate::err::Error;
use crate::sql::literal::Literal;
use crate::sql::value::Value;
pub fn or(a: Literal, b: Literal) -> Result<Literal, Error> {
match a.as_bool() {
true => Ok(a),
false => Ok(b),
}
}
pub fn and(a: Literal, b: Literal) -> Result<Literal, Error> {
match a.as_bool() {
true => match b.as_bool() {
_ => Ok(b),
},
false => Ok(a),
}
}
pub fn add(a: &Literal, b: &Literal) -> Result<Literal, Error> {
let a = a.as_number();
let b = b.as_number();
Ok(Literal::from(a + b))
}
pub fn sub(a: &Literal, b: &Literal) -> Result<Literal, Error> {
let a = a.as_number();
let b = b.as_number();
Ok(Literal::from(a - b))
}
pub fn mul(a: &Literal, b: &Literal) -> Result<Literal, Error> {
let a = a.as_number();
let b = b.as_number();
Ok(Literal::from(a * b))
}
pub fn div(a: &Literal, b: &Literal) -> Result<Literal, Error> {
let a = a.as_number();
let b = b.as_number();
Ok(Literal::from(a / b))
}
pub fn equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::None => Ok(Literal::from(b.is_none() == true)),
Literal::Null => Ok(Literal::from(b.is_null() == true)),
Literal::Void => Ok(Literal::from(b.is_void() == true)),
Literal::True => Ok(Literal::from(b.is_true() == true)),
Literal::False => Ok(Literal::from(b.is_false() == true)),
Literal::Int(v) => Ok(Literal::from(v == &b.as_int())),
Literal::Float(v) => Ok(Literal::from(v == &b.as_float())),
Literal::Thing(v) => match b {
Literal::Thing(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::True),
},
Literal::Regex(v) => match b {
Literal::Regex(w) => Ok(Literal::from(v == w)),
Literal::Strand(w) => match v.value {
Some(ref r) => Ok(Literal::from(r.is_match(w.value.as_str()) == true)),
None => Ok(Literal::False),
},
_ => Ok(Literal::False),
},
Literal::Point(v) => match b {
Literal::Point(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
Literal::Array(v) => match b {
Literal::Array(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
Literal::Object(v) => match b {
Literal::Object(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
Literal::Strand(v) => match b {
Literal::Strand(w) => Ok(Literal::from(v == w)),
Literal::Regex(w) => match w.value {
Some(ref r) => Ok(Literal::from(r.is_match(v.value.as_str()) == true)),
None => Ok(Literal::False),
},
_ => Ok(Literal::from(v == &b.as_strand())),
},
Literal::Number(v) => Ok(Literal::from(v == &b.as_number())),
Literal::Polygon(v) => match b {
Literal::Polygon(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
Literal::Duration(v) => match b {
Literal::Duration(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
Literal::Datetime(v) => match b {
Literal::Datetime(w) => Ok(Literal::from(v == w)),
_ => Ok(Literal::False),
},
_ => unreachable!(),
}
}
pub fn not_equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::None => Ok(Literal::from(b.is_none() != true)),
Literal::Null => Ok(Literal::from(b.is_null() != true)),
Literal::Void => Ok(Literal::from(b.is_void() != true)),
Literal::True => Ok(Literal::from(b.is_true() != true)),
Literal::False => Ok(Literal::from(b.is_false() != true)),
Literal::Int(v) => Ok(Literal::from(v != &b.as_int())),
Literal::Float(v) => Ok(Literal::from(v != &b.as_float())),
Literal::Thing(v) => match b {
Literal::Thing(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Regex(v) => match b {
Literal::Regex(w) => Ok(Literal::from(v != w)),
Literal::Strand(w) => match v.value {
Some(ref r) => Ok(Literal::from(r.is_match(w.value.as_str()) != true)),
None => Ok(Literal::True),
},
_ => Ok(Literal::True),
},
Literal::Point(v) => match b {
Literal::Point(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Array(v) => match b {
Literal::Array(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Object(v) => match b {
Literal::Object(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Number(v) => match b {
Literal::Number(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Strand(v) => match b {
Literal::Strand(w) => Ok(Literal::from(v != w)),
Literal::Regex(w) => match w.value {
Some(ref r) => Ok(Literal::from(r.is_match(v.value.as_str()) != true)),
None => Ok(Literal::False),
},
_ => Ok(Literal::from(v != &b.as_strand())),
},
Literal::Polygon(v) => match b {
Literal::Polygon(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Duration(v) => match b {
Literal::Duration(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
Literal::Datetime(v) => match b {
Literal::Datetime(w) => Ok(Literal::from(v != w)),
_ => Ok(Literal::True),
},
_ => unreachable!(),
}
}
pub fn all_equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(ref v) => match v.value.iter().all(|x| match x {
Value::Literal(v) => equal(v, b).is_ok(),
_ => unreachable!(),
}) {
true => Ok(Literal::True),
false => Ok(Literal::False),
},
_ => equal(a, b),
}
}
pub fn any_equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(ref v) => match v.value.iter().any(|x| match x {
Value::Literal(v) => equal(v, b).is_ok(),
_ => unreachable!(),
}) {
true => Ok(Literal::True),
false => Ok(Literal::False),
},
_ => equal(a, b),
}
}
pub fn like(a: &Literal, b: &Literal) -> Result<Literal, Error> {
todo!()
}
pub fn not_like(a: &Literal, b: &Literal) -> Result<Literal, Error> {
todo!()
}
pub fn all_like(a: &Literal, b: &Literal) -> Result<Literal, Error> {
todo!()
}
pub fn any_like(a: &Literal, b: &Literal) -> Result<Literal, Error> {
todo!()
}
pub fn less_than(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a.lt(&b) {
true => Ok(Literal::True),
false => Ok(Literal::False),
}
}
pub fn less_than_or_equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a.le(&b) {
true => Ok(Literal::True),
false => Ok(Literal::False),
}
}
pub fn more_than(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a.gt(&b) {
true => Ok(Literal::True),
false => Ok(Literal::False),
}
}
pub fn more_than_or_equal(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a.ge(&b) {
true => Ok(Literal::True),
false => Ok(Literal::False),
}
}
pub fn contain(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn not_contain(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn contain_all(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn contain_some(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn contain_none(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match a {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn inside(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn not_inside(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn inside_all(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn inside_some(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn inside_none(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Array(v) => todo!(),
Literal::Strand(v) => todo!(),
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
pub fn intersects(a: &Literal, b: &Literal) -> Result<Literal, Error> {
match b {
Literal::Polygon(v) => todo!(),
_ => Ok(Literal::False),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn or_true() {
let one = Literal::from(1);
let two = Literal::from(2);
let res = or(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn or_false_one() {
let one = Literal::from(0);
let two = Literal::from(1);
let res = or(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn or_false_two() {
let one = Literal::from(1);
let two = Literal::from(0);
let res = or(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn and_true() {
let one = Literal::from(1);
let two = Literal::from(2);
let res = and(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("2", format!("{}", out));
}
#[test]
fn and_false_one() {
let one = Literal::from(0);
let two = Literal::from(1);
let res = and(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("0", format!("{}", out));
}
#[test]
fn and_false_two() {
let one = Literal::from(1);
let two = Literal::from(0);
let res = and(one, two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("0", format!("{}", out));
}
#[test]
fn add_basic() {
let one = Literal::from(5);
let two = Literal::from(4);
let res = add(&one, &two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("9", format!("{}", out));
}
#[test]
fn sub_basic() {
let one = Literal::from(5);
let two = Literal::from(4);
let res = sub(&one, &two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1", format!("{}", out));
}
#[test]
fn mul_basic() {
let one = Literal::from(5);
let two = Literal::from(4);
let res = mul(&one, &two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("20", format!("{}", out));
}
#[test]
fn div_basic() {
let one = Literal::from(5);
let two = Literal::from(4);
let res = div(&one, &two);
assert!(res.is_ok());
let out = res.unwrap();
assert_eq!("1.25", format!("{}", out));
}
}

View file

@ -1,4 +1,4 @@
use failure::Error;
use anyhow::Error;
pub fn init(opts: &clap::ArgMatches) -> Result<(), Error> {
let pth = opts.value_of("path").unwrap();

16
src/lib.rs Normal file
View file

@ -0,0 +1,16 @@
#[macro_use]
extern crate maplit;
#[macro_use]
extern crate log;
mod ctx;
mod dbs;
mod doc;
mod err;
mod fnc;
mod kvs;
mod sql;
fn main() {
// cli::init(); // Initiate the command line
}

View file

@ -1,13 +1,14 @@
#[macro_use]
extern crate failure;
#[macro_use]
extern crate maplit;
#[macro_use]
extern crate log;
mod cli;
mod ctx;
mod dbs;
mod doc;
mod err;
mod fnc;
mod kvs;
mod sql;
mod web;

View file

@ -5,7 +5,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Algorithm {
Es256,
Es384,

View file

@ -1,39 +1,94 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::doc::Document;
use crate::err::Error;
use crate::sql::comment::mightbespace;
use crate::sql::common::commas;
use crate::sql::expression::{expression, Expression};
use crate::sql::expression::expression;
use crate::sql::literal::Literal;
use crate::sql::value::Value;
use nom::bytes::complete::tag;
use nom::combinator::opt;
use nom::multi::separated_list;
use nom::multi::separated_list0;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Array(Vec<Expression>);
const NAME: &'static str = "Array";
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Array {
pub value: Vec<Value>,
}
impl fmt::Display for Array {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"[{}]",
self.0
.iter()
.map(|ref v| format!("{}", v))
.collect::<Vec<_>>()
.join(", ")
self.value.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
)
}
}
impl dbs::Process for Array {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
self.value
.iter()
.map(|v| match v.process(ctx, exe, doc) {
Ok(v) => Ok(Value::from(v)),
Err(e) => Err(e),
})
.collect::<Result<Vec<_>, _>>()
.map(|v| {
Literal::Array(Array {
value: v,
})
})
}
}
impl Serialize for Array {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_some(&self.value)
} else {
let mut val = serializer.serialize_struct(NAME, 1)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
pub fn array(i: &str) -> IResult<&str, Array> {
let (i, _) = tag("[")(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = separated_list(commas, expression)(i)?;
let (i, v) = separated_list0(commas, item)(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = opt(tag(","))(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag("]")(i)?;
Ok((i, Array(v)))
Ok((
i,
Array {
value: v,
},
))
}
fn item(i: &str) -> IResult<&str, Value> {
let (i, v) = expression(i)?;
Ok((i, Value::from(v)))
}
#[cfg(test)]
@ -48,7 +103,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("[1, 2, 3]", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
#[test]
@ -58,7 +113,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("[1, 2, 3]", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
#[test]
@ -68,6 +123,6 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("[1, 2, 3 + 1]", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
}

View file

@ -5,7 +5,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Base {
Kv,
Ns,

View file

@ -5,6 +5,7 @@ use nom::character::complete::multispace0;
use nom::character::is_alphanumeric;
use nom::combinator::map;
use nom::error::ErrorKind;
use nom::error::Error;
use nom::multi::many1;
use nom::IResult;
use std::ops::RangeBounds;
@ -75,6 +76,6 @@ pub fn take_digits_range(i: &str, n: usize, range: impl RangeBounds<u32>) -> IRe
if range.contains(&v) {
Ok((i, v))
} else {
Err(nom::Err::Error((i, ErrorKind::Eof)))
Err(nom::Err::Error(Error::new(i, ErrorKind::Eof)))
}
}

View file

@ -5,7 +5,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Cond {
pub expr: Expression,
}
@ -20,7 +20,12 @@ pub fn cond(i: &str) -> IResult<&str, Cond> {
let (i, _) = tag_no_case("WHERE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = expression(i)?;
Ok((i, Cond { expr: v }))
Ok((
i,
Cond {
expr: v,
},
))
}
#[cfg(test)]
@ -43,9 +48,6 @@ mod tests {
let res = cond(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
"WHERE field = true AND other.field = false",
format!("{}", out)
);
assert_eq!("WHERE field = true AND other.field = false", format!("{}", out));
}
}

View file

@ -8,12 +8,12 @@ use crate::sql::object::{object, Object};
use crate::sql::operator::{assigner, Operator};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Data {
SetExpression(Vec<(Idiom, Operator, Expression)>),
DiffExpression(Array),
@ -46,7 +46,7 @@ pub fn data(i: &str) -> IResult<&str, Data> {
fn set(i: &str) -> IResult<&str, Data> {
let (i, _) = tag_no_case("SET")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = separated_nonempty_list(commas, |i| {
let (i, v) = separated_list1(commas, |i| {
let (i, l) = idiom(i)?;
let (i, _) = mightbespace(i)?;
let (i, o) = assigner(i)?;

View file

@ -5,29 +5,51 @@ use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::sequence::delimited;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Datetime(DateTime<Utc>);
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Datetime {
pub value: DateTime<Utc>,
}
impl Default for Datetime {
fn default() -> Self {
Datetime(Utc::now())
Datetime {
value: Utc::now(),
}
}
}
impl<'a> From<&'a str> for Datetime {
fn from(s: &str) -> Self {
datetime_raw(s).unwrap().1
match datetime_raw(s) {
Ok((_, v)) => v,
Err(_) => Datetime::default(),
}
}
}
impl fmt::Display for Datetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{:?}\"", self.0)
write!(f, "\"{:?}\"", self.value)
}
}
impl Serialize for Datetime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_some(&self.value)
} else {
let mut val = serializer.serialize_struct("Datetime", 1)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
@ -38,7 +60,7 @@ pub fn datetime(i: &str) -> IResult<&str, Datetime> {
))(i)
}
fn datetime_raw(i: &str) -> IResult<&str, Datetime> {
pub fn datetime_raw(i: &str) -> IResult<&str, Datetime> {
alt((nano, time, date))(i)
}
@ -50,7 +72,12 @@ fn date(i: &str) -> IResult<&str, Datetime> {
let (i, day) = day(i)?;
let d = Utc.ymd(year, mon, day).and_hms(0, 0, 0);
Ok((i, Datetime(d)))
Ok((
i,
Datetime {
value: d,
},
))
}
fn time(i: &str) -> IResult<&str, Datetime> {
@ -71,11 +98,15 @@ fn time(i: &str) -> IResult<&str, Datetime> {
Some(z) => {
let d = z.ymd(year, mon, day).and_hms(hour, min, sec);
let d = d.with_timezone(&Utc);
Datetime(d)
Datetime {
value: d,
}
}
None => {
let d = Utc.ymd(year, mon, day).and_hms(hour, min, sec);
Datetime(d)
Datetime {
value: d,
}
}
};
@ -101,11 +132,15 @@ fn nano(i: &str) -> IResult<&str, Datetime> {
Some(z) => {
let d = z.ymd(year, mon, day).and_hms_nano(hour, min, sec, nano);
let d = d.with_timezone(&Utc);
Datetime(d)
Datetime {
value: d,
}
}
None => {
let d = Utc.ymd(year, mon, day).and_hms_nano(hour, min, sec, nano);
Datetime(d)
Datetime {
value: d,
}
}
};

View file

@ -1,52 +0,0 @@
use crate::sql::literal::{literal, Literal};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum Definition {
All,
Void,
Empty,
Filled,
Literal(Literal),
}
impl<'a> From<&'a str> for Definition {
fn from(s: &str) -> Self {
definition(s).unwrap().1
}
}
impl From<Literal> for Definition {
fn from(v: Literal) -> Self {
Definition::Literal(v)
}
}
impl fmt::Display for Definition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Definition::All => write!(f, "*"),
Definition::Void => write!(f, "VOID"),
Definition::Empty => write!(f, "EMPTY"),
Definition::Filled => write!(f, "FILLED"),
Definition::Literal(v) => write!(f, "{}", v),
}
}
}
pub fn definition(i: &str) -> IResult<&str, Definition> {
alt((
map(tag_no_case("*"), |_| Definition::All),
map(tag_no_case("VOID"), |_| Definition::Void),
map(tag_no_case("EMPTY"), |_| Definition::Empty),
map(tag_no_case("FILLED"), |_| Definition::Filled),
map(tag_no_case("MISSING"), |_| Definition::Empty),
map(literal, |v| Definition::Literal(v)),
))(i)
}

View file

@ -2,12 +2,13 @@ use nom::branch::alt;
use nom::bytes::complete::is_a;
use nom::bytes::complete::tag;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use std::time;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Duration {
pub input: String,
pub value: time::Duration,
@ -15,7 +16,10 @@ pub struct Duration {
impl<'a> From<&'a str> for Duration {
fn from(s: &str) -> Self {
duration(s).unwrap().1
match duration(s) {
Ok((_, v)) => v,
Err(_) => Duration::default(),
}
}
}
@ -25,7 +29,27 @@ impl fmt::Display for Duration {
}
}
impl Serialize for Duration {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_some(&self.value)
} else {
let mut val = serializer.serialize_struct("Duration", 2)?;
val.serialize_field("input", &self.input)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
pub fn duration(i: &str) -> IResult<&str, Duration> {
duration_raw(i)
}
pub fn duration_raw(i: &str) -> IResult<&str, Duration> {
let (i, v) = part(i)?;
let (i, u) = unit(i)?;
Ok((
@ -54,16 +78,7 @@ fn part(i: &str) -> IResult<&str, u64> {
}
fn unit(i: &str) -> IResult<&str, &str> {
alt((
tag("ns"),
tag("µs"),
tag("ms"),
tag("s"),
tag("m"),
tag("h"),
tag("d"),
tag("w"),
))(i)
alt((tag("ns"), tag("µs"), tag("ms"), tag("s"), tag("m"), tag("h"), tag("d"), tag("w")))(i)
}
#[cfg(test)]
@ -110,4 +125,24 @@ mod tests {
assert_eq!("86400s", format!("{}", out));
assert_eq!(out.value, Duration::from("1d").value);
}
#[test]
fn duration_days() {
let sql = "5d";
let res = duration(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("5d", format!("{}", out));
assert_eq!(out.value, Duration::from("5d").value);
}
#[test]
fn duration_weeks() {
let sql = "4w";
let res = duration(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("4w", format!("{}", out));
assert_eq!(out.value, Duration::from("4w").value);
}
}

View file

@ -1,6 +1,11 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::doc::Document;
use crate::err::Error;
use crate::fnc;
use crate::sql::comment::mightbespace;
use crate::sql::definition::{definition, Definition};
use crate::sql::literal::Literal;
use crate::sql::literal::{literal, Literal};
use crate::sql::operator::{operator, Operator};
use nom::branch::alt;
use nom::IResult;
@ -8,14 +13,16 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Expression {
#[serde(skip_serializing_if = "Option::is_none")]
pub lhs: Option<Box<Definition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub op: Option<Operator>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rhs: Option<Box<Expression>>,
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Expression {
Single(Box<Literal>),
Binary(Box<Literal>, Operator, Box<Expression>),
}
impl Default for Expression {
fn default() -> Expression {
Expression::Single(Box::new(Literal::Null))
}
}
impl<'a> From<&'a str> for Expression {
@ -26,57 +33,153 @@ impl<'a> From<&'a str> for Expression {
impl From<Literal> for Expression {
fn from(v: Literal) -> Self {
Expression {
lhs: Some(Box::new(Definition::from(v))),
op: None,
rhs: None,
}
Expression::Single(Box::new(v))
}
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref lhs) = self.lhs {
write!(f, "{}", lhs)?
match self {
Expression::Single(ref l) => write!(f, "{}", l),
Expression::Binary(ref l, ref o, ref r) => write!(f, "{} {} {}", l, o, r),
}
if let Some(ref op) = self.op {
write!(f, " {} ", op)?
}
}
impl dbs::Process for Expression {
fn process(
&self,
ctx: &Parent,
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() {
true => return Ok(l), // No need to continue
_ => {} // Continue
},
Operator::And => match l.as_bool() {
false => return Ok(l), // No need to continue
_ => {} // Continue
},
_ => {} // Continue
}
let r = r.process(ctx, exe, doc)?;
match 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::Equal => fnc::operate::equal(&l, &r),
Operator::NotEqual => fnc::operate::not_equal(&l, &r),
Operator::AllEqual => fnc::operate::all_equal(&l, &r),
Operator::AnyEqual => fnc::operate::any_equal(&l, &r),
Operator::Like => fnc::operate::like(&l, &r),
Operator::NotLike => fnc::operate::not_like(&l, &r),
Operator::AllLike => fnc::operate::all_like(&l, &r),
Operator::AnyLike => fnc::operate::any_like(&l, &r),
Operator::LessThan => fnc::operate::less_than(&l, &r),
Operator::LessThanOrEqual => fnc::operate::less_than_or_equal(&l, &r),
Operator::MoreThan => fnc::operate::more_than(&l, &r),
Operator::MoreThanOrEqual => fnc::operate::more_than_or_equal(&l, &r),
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::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::NoneInside => fnc::operate::inside_none(&l, &r),
Operator::Intersects => fnc::operate::intersects(&l, &r),
_ => unreachable!(),
}
}
}
if let Some(ref rhs) = self.rhs {
write!(f, "{}", rhs)?
}
Ok(())
}
}
pub fn expression(i: &str) -> IResult<&str, Expression> {
alt((dual, lone))(i)
alt((binary, single))(i)
}
fn dual(i: &str) -> IResult<&str, Expression> {
let (i, l) = definition(i)?;
fn binary(i: &str) -> IResult<&str, Expression> {
let (i, l) = literal(i)?;
let (i, _) = mightbespace(i)?;
let (i, o) = operator(i)?;
let (i, _) = mightbespace(i)?;
let (i, r) = expression(i)?;
Ok((
i,
Expression {
lhs: Some(Box::new(l)),
op: Some(o),
rhs: Some(Box::new(r)),
},
))
Ok((i, Expression::Binary(Box::new(l), o, Box::new(r))))
}
fn lone(i: &str) -> IResult<&str, Expression> {
let (i, l) = definition(i)?;
Ok((
i,
Expression {
lhs: Some(Box::new(l)),
op: None,
rhs: None,
},
))
fn single(i: &str) -> IResult<&str, Expression> {
let (i, l) = literal(i)?;
Ok((i, Expression::Single(Box::new(l))))
}
#[cfg(test)]
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() {
let sql = "true AND false";
let res = expression(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("true AND false", format!("{}", out));
}
#[test]
fn expression_left_opened() {
let sql = "3 * 3 * 3 = 27";
let res = expression(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("3 * 3 * 3 = 27", format!("{}", out));
}
#[test]
fn expression_left_closed() {
let sql = "(3 * 3 * 3) = 27";
let res = expression(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("(3 * 3 * 3) = 27", format!("{}", out));
}
#[test]
fn expression_both_opened() {
let sql = "3 * 3 * 3 = 3 * 3 * 3";
let res = expression(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("3 * 3 * 3 = 3 * 3 * 3", format!("{}", out));
}
#[test]
fn expression_both_closed() {
let sql = "(3 * 3 * 3) = (3 * 3 * 3)";
let res = expression(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("(3 * 3 * 3) = (3 * 3 * 3)", format!("{}", out));
}
}

View file

@ -2,12 +2,12 @@ use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::idiom::{idiom, Idiom};
use nom::bytes::complete::tag_no_case;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Fetchs(Vec<Fetch>);
impl fmt::Display for Fetchs {
@ -15,16 +15,12 @@ impl fmt::Display for Fetchs {
write!(
f,
"FETCH {}",
self.0
.iter()
.map(|ref v| format!("{}", v))
.collect::<Vec<_>>()
.join(", ")
self.0.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
)
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Fetch {
pub fetch: Idiom,
}
@ -38,13 +34,18 @@ impl fmt::Display for Fetch {
pub fn fetch(i: &str) -> IResult<&str, Fetchs> {
let (i, _) = tag_no_case("FETCH")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = separated_nonempty_list(commas, fetch_raw)(i)?;
let (i, v) = separated_list1(commas, fetch_raw)(i)?;
Ok((i, Fetchs(v)))
}
fn fetch_raw(i: &str) -> IResult<&str, Fetch> {
let (i, v) = idiom(i)?;
Ok((i, Fetch { fetch: v }))
Ok((
i,
Fetch {
fetch: v,
},
))
}
#[cfg(test)]

View file

@ -2,14 +2,14 @@ use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::expression::{expression, Expression};
use crate::sql::idiom::{idiom, Idiom};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Fields(Vec<Field>);
impl fmt::Display for Fields {
@ -19,45 +19,54 @@ impl fmt::Display for Fields {
}
pub fn fields(i: &str) -> IResult<&str, Fields> {
let (i, v) = separated_nonempty_list(commas, field)(i)?;
let (i, v) = separated_list1(commas, field)(i)?;
Ok((i, Fields(v)))
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Field {
pub field: Expression,
#[serde(skip_serializing_if = "Option::is_none")]
pub alias: Option<Idiom>,
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Field {
All,
Alone(Expression),
Alias(Expression, Idiom),
}
impl Default for Field {
fn default() -> Field {
Field::All
}
}
impl fmt::Display for Field {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref a) = self.alias {
write!(f, "{} AS {}", self.field, a)
} else {
write!(f, "{}", self.field)
match self {
Field::All => write!(f, "*"),
Field::Alone(ref e) => write!(f, "{}", e),
Field::Alias(ref e, ref a) => write!(f, "{} AS {}", e, a),
}
}
}
pub fn field(i: &str) -> IResult<&str, Field> {
let (i, f) = expression(i)?;
let (i, a) = opt(alias)(i)?;
Ok((
i,
Field {
field: f,
alias: a,
},
))
alt((all, alias, alone))(i)
}
fn alias(i: &str) -> IResult<&str, Idiom> {
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)?;
Ok((i, Field::Alone(f)))
}
fn alias(i: &str) -> IResult<&str, Field> {
let (i, f) = expression(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("AS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = idiom(i)?;
Ok((i, v))
let (i, a) = idiom(i)?;
Ok((i, Field::Alias(f, a)))
}
#[cfg(test)]
@ -65,6 +74,15 @@ mod tests {
use super::*;
#[test]
fn field_all() {
let sql = "*";
let res = fields(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("*", format!("{}", out));
}
#[test]
fn field_single() {
let sql = "field";

View file

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Filter {
All,
Last,

View file

@ -1,54 +1,129 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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, Expression};
use crate::sql::literal::simple;
use crate::sql::literal::Literal;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::multi::separated_list;
use nom::multi::separated_list0;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Function {
pub name: String,
pub args: Vec<Expression>,
pub cast: bool,
pub func: bool,
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Function {
Future(Expression),
Cast(String, Expression),
Normal(String, Vec<Expression>),
}
impl PartialOrd for Function {
#[inline]
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
unreachable!()
}
}
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.func {
return write!(
match self {
Function::Future(ref e) => write!(f, "fn() -> {{ {} }}", e),
Function::Cast(ref s, ref e) => write!(f, "<{}>{}", s, e),
Function::Normal(ref s, ref e) => write!(
f,
"{}() -> {{ {} }}",
self.name,
self.args.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "),
);
"{}({})",
s,
e.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
),
}
if self.cast {
return write!(
f,
"<{}>{}",
self.name,
self.args.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "),
);
}
}
impl dbs::Process for Function {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
match self {
Function::Future(ref e) => {
let a = e.process(ctx, exe, doc)?;
fnc::future::run(ctx, a)
}
Function::Cast(ref s, ref e) => {
let a = e.process(ctx, exe, doc)?;
fnc::cast::run(ctx, s, a)
}
Function::Normal(ref s, ref e) => {
let mut a: Vec<Literal> = vec![];
for v in e {
let v = v.process(ctx, exe, doc)?;
a.push(v);
}
fnc::run(ctx, s, a)
}
}
write!(
f,
"{}({})",
self.name,
self.args.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", "),
)
}
}
pub fn function(i: &str) -> IResult<&str, Function> {
alt((casts, future, normal))(i)
}
fn future(i: &str) -> IResult<&str, Function> {
let (i, _) = tag("fn()")(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, _) = mightbespace(i)?;
let (i, _) = tag("}")(i)?;
Ok((i, Function::Future(v)))
}
fn casts(i: &str) -> IResult<&str, Function> {
let (i, _) = tag("<")(i)?;
let (i, s) = function_casts(i)?;
let (i, _) = tag(">")(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = expression(i)?;
Ok((i, Function::Cast(s.to_string(), v)))
}
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, _) = mightbespace(i)?;
let (i, _) = tag(")")(i)?;
Ok((i, Function::Normal(s.to_string(), v)))
}
fn function_casts(i: &str) -> IResult<&str, &str> {
alt((
tag("bool"),
tag("int"),
tag("float"),
tag("string"),
tag("number"),
tag("decimal"),
tag("datetime"),
tag("duration"),
))(i)
}
fn function_names(i: &str) -> IResult<&str, &str> {
alt((
casts,
future,
function_all,
function_array,
function_count,
function_geo,
@ -64,314 +139,233 @@ pub fn function(i: &str) -> IResult<&str, Function> {
))(i)
}
fn casts(i: &str) -> IResult<&str, Function> {
fn function_array(i: &str) -> IResult<&str, &str> {
alt((
cast("bool"),
cast("int16"),
cast("int32"),
cast("int64"),
cast("int128"),
cast("uint16"),
cast("uint32"),
cast("uint64"),
cast("uint128"),
cast("float32"),
cast("float64"),
cast("decimal"),
cast("number"),
cast("string"),
cast("binary"),
cast("bytes"),
cast("datetime"),
cast("duration"),
tag("array::difference"),
tag("array::distinct"),
tag("array::intersect"),
tag("array::union"),
))(i)
}
fn future(i: &str) -> IResult<&str, Function> {
let (i, _) = tag("fn()")(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, _) = mightbespace(i)?;
let (i, _) = tag("}")(i)?;
Ok((
i,
Function {
name: String::from("fn"),
args: vec![v],
cast: false,
func: true,
},
))
}
fn function_all(i: &str) -> IResult<&str, Function> {
alt((func("if"), func("either")))(i)
}
fn function_array(i: &str) -> IResult<&str, Function> {
fn function_count(i: &str) -> IResult<&str, &str> {
alt((
func("array::difference"),
func("array::distinct"),
func("array::intersect"),
func("array::union"),
tag("count::all"),
tag("count::if"),
tag("count::not"),
tag("count::oneof"),
tag("count::between"),
tag("count"),
))(i)
}
fn function_count(i: &str) -> IResult<&str, Function> {
alt((func("count"), func("count::if"), func("count::not")))(i)
}
fn function_geo(i: &str) -> IResult<&str, Function> {
fn function_geo(i: &str) -> IResult<&str, &str> {
alt((
func("geo::circle"),
func("geo::distance"),
func("geo::point"),
func("geo::polygon"),
func("geo::hash::decode"),
func("geo::hash::encode"),
tag("geo::distance"),
tag("geo::latitude"),
tag("geo::longitude"),
tag("geo::hash::decode"),
tag("geo::hash::encode"),
))(i)
}
fn function_hash(i: &str) -> IResult<&str, Function> {
fn function_hash(i: &str) -> IResult<&str, &str> {
alt((
func("hash::md5"),
func("hash::sha1"),
func("hash::sha256"),
func("hash::sha512"),
func("hash::bcrypt"),
func("hash::bcrypt::compare"),
func("hash::bcrypt::generate"),
func("hash::scrypt"),
func("hash::scrypt::compare"),
func("hash::scrypt::generate"),
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, Function> {
fn function_http(i: &str) -> IResult<&str, &str> {
alt((
func("http::head"),
func("http::get"),
func("http::put"),
func("http::post"),
func("http::patch"),
func("http::delete"),
func("http::async::head"),
func("http::async::get"),
func("http::async::put"),
func("http::async::post"),
func("http::async::patch"),
func("http::async::delete"),
tag("http::head"),
tag("http::get"),
tag("http::put"),
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)
}
fn function_is(i: &str) -> IResult<&str, Function> {
fn function_is(i: &str) -> IResult<&str, &str> {
alt((
func("is::alpha"),
func("is::alphanum"),
func("is::ascii"),
func("is::domain"),
func("is::email"),
func("is::hexadecimal"),
func("is::latitude"),
func("is::longitude"),
func("is::numeric"),
func("is::semver"),
func("is::uuid"),
tag("is::alphanum"),
tag("is::alpha"),
tag("is::ascii"),
tag("is::domain"),
tag("is::email"),
tag("is::hexadecimal"),
tag("is::latitude"),
tag("is::longitude"),
tag("is::numeric"),
tag("is::semver"),
tag("is::uuid"),
))(i)
}
fn function_math(i: &str) -> IResult<&str, Function> {
fn function_math(i: &str) -> IResult<&str, &str> {
alt((
alt((
func("math::abs"),
func("math::bottom"),
func("math::ceil"),
func("math::correlation"),
func("math::count"),
func("math::covariance"),
func("math::fixed"),
func("math::floor"),
func("math::geometricmean"),
func("math::harmonicmean"),
func("math::interquartile"),
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((
func("math::max"),
func("math::mean"),
func("math::median"),
func("math::midhinge"),
func("math::min"),
func("math::mode"),
tag("math::max"),
tag("math::mean"),
tag("math::median"),
tag("math::midhinge"),
tag("math::min"),
tag("math::mode"),
)),
alt((
func("math::nearestrank"),
func("math::percentile"),
func("math::round"),
func("math::sample"),
func("math::spread"),
func("math::sqrt"),
func("math::stddev"),
func("math::sum"),
func("math::top"),
func("math::trimean"),
func("math::variance"),
tag("math::nearestrank"),
tag("math::percentile"),
tag("math::round"),
tag("math::sample"),
tag("math::spread"),
tag("math::sqrt"),
tag("math::stddev"),
tag("math::sum"),
tag("math::top"),
tag("math::trimean"),
tag("math::variance"),
)),
))(i)
}
fn function_parse(i: &str) -> IResult<&str, Function> {
fn function_parse(i: &str) -> IResult<&str, &str> {
alt((
func("parse::email::domain"),
func("parse::email::user"),
func("parse::url::domain"),
func("parse::url::host"),
func("parse::url::port"),
func("parse::url::path"),
tag("parse::email::domain"),
tag("parse::email::user"),
tag("parse::url::domain"),
tag("parse::url::host"),
tag("parse::url::port"),
tag("parse::url::path"),
))(i)
}
fn function_rand(i: &str) -> IResult<&str, Function> {
fn function_rand(i: &str) -> IResult<&str, &str> {
alt((
alt((
func("rand"),
func("guid"),
func("uuid"),
func("rand::bool"),
func("rand::guid"),
func("rand::uuid"),
func("rand::enum"),
func("rand::time"),
func("rand::string"),
func("rand::integer"),
func("rand::decimal"),
func("rand::sentence"),
func("rand::paragraph"),
tag("guid"),
tag("uuid"),
tag("rand::bool"),
tag("rand::guid"),
tag("rand::uuid"),
tag("rand::enum"),
tag("rand::time"),
tag("rand::string"),
tag("rand::integer"),
tag("rand::decimal"),
tag("rand::sentence"),
tag("rand::paragraph"),
)),
alt((
func("rand::person::email"),
func("rand::person::phone"),
func("rand::person::fullname"),
func("rand::person::firstname"),
func("rand::person::lastname"),
func("rand::person::username"),
func("rand::person::jobtitle"),
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((
func("rand::location::name"),
func("rand::location::address"),
func("rand::location::street"),
func("rand::location::city"),
func("rand::location::state"),
func("rand::location::county"),
func("rand::location::zipcode"),
func("rand::location::postcode"),
func("rand::location::country"),
func("rand::location::altitude"),
func("rand::location::latitude"),
func("rand::location::longitude"),
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"),
))(i)
}
fn function_string(i: &str) -> IResult<&str, Function> {
fn function_string(i: &str) -> IResult<&str, &str> {
alt((
func("string::concat"),
func("string::contains"),
func("string::endsWith"),
func("string::format"),
func("string::includes"),
func("string::join"),
func("string::length"),
func("string::lowercase"),
func("string::repeat"),
func("string::replace"),
func("string::reverse"),
func("string::search"),
func("string::slice"),
func("string::slug"),
func("string::split"),
func("string::startsWith"),
func("string::substr"),
func("string::trim"),
func("string::uppercase"),
func("string::words"),
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"),
tag("string::startsWith"),
tag("string::substr"),
tag("string::trim"),
tag("string::uppercase"),
tag("string::words"),
))(i)
}
fn function_time(i: &str) -> IResult<&str, Function> {
fn function_time(i: &str) -> IResult<&str, &str> {
alt((
func("time::now"),
func("time::add"),
func("time::age"),
func("time::floor"),
func("time::round"),
func("time::day"),
func("time::hour"),
func("time::mins"),
func("time::month"),
func("time::nano"),
func("time::secs"),
func("time::unix"),
func("time::wday"),
func("time::week"),
func("time::yday"),
func("time::year"),
tag("time::now"),
tag("time::add"),
tag("time::age"),
tag("time::floor"),
tag("time::round"),
tag("time::day"),
tag("time::hour"),
tag("time::mins"),
tag("time::month"),
tag("time::nano"),
tag("time::secs"),
tag("time::unix"),
tag("time::wday"),
tag("time::week"),
tag("time::yday"),
tag("time::year"),
))(i)
}
fn function_type(i: &str) -> IResult<&str, Function> {
fn function_type(i: &str) -> IResult<&str, &str> {
alt((
func("type::batch"),
func("type::model"),
func("type::regex"),
func("type::table"),
func("type::thing"),
tag("type::batch"),
tag("type::model"),
tag("type::point"),
tag("type::polygon"),
tag("type::regex"),
tag("type::table"),
tag("type::thing"),
))(i)
}
fn cast<'b, 'a: 'b>(f: &'a str) -> impl Fn(&'b str) -> IResult<&'b str, Function> where {
move |i: &'b str| {
let (i, _) = tag("<")(i)?;
let (i, n) = tag(f)(i)?;
let (i, _) = tag(">")(i)?;
let (i, _) = mightbespace(i)?;
let (i, a) = simple(i)?;
Ok((
i,
Function {
name: n.to_string(),
args: vec![Expression::from(a)],
cast: true,
func: false,
},
))
}
}
fn func<'b, 'a: 'b>(f: &'a str) -> impl Fn(&'b str) -> IResult<&'b str, Function> where {
move |i: &'b str| {
let (i, n) = tag(f)(i)?;
let (i, _) = tag("(")(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = separated_list(commas, expression)(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag(")")(i)?;
Ok((
i,
Function {
name: n.to_string(),
args: v,
cast: false,
func: false,
},
))
}
}
#[cfg(test)]
mod tests {
@ -384,15 +378,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("count()", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("count"),
args: vec![],
cast: false,
func: false,
}
);
assert_eq!(out, Function::Normal(String::from("count"), vec![]));
}
#[test]
@ -402,15 +388,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("count::if()", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("count::if"),
args: vec![],
cast: false,
func: false,
}
);
assert_eq!(out, Function::Normal(String::from("count::if"), vec![]));
}
#[test]
@ -422,31 +400,18 @@ mod tests {
assert_eq!("is::numeric(NULL)", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("is::numeric"),
args: vec![Expression::from("null")],
cast: false,
func: false,
}
Function::Normal(String::from("is::numeric"), vec![Expression::from("null")])
);
}
#[test]
fn function_casting_number() {
let sql = "<uint64>1.2345";
let sql = "<int>1.2345";
let res = function(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<uint64>1.2345", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("uint64"),
args: vec![Expression::from("1.2345")],
cast: true,
func: false,
}
);
assert_eq!("<int>1.2345", format!("{}", out));
assert_eq!(out, Function::Cast(String::from("int"), Expression::from("1.2345")));
}
#[test]
@ -456,15 +421,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("<string>1.2345", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("string"),
args: vec![Expression::from("1.2345")],
cast: true,
func: false,
}
);
assert_eq!(out, Function::Cast(String::from("string"), Expression::from("1.2345")));
}
#[test]
@ -474,14 +431,6 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("fn() -> { 1.2345 + 5.4321 }", format!("{}", out));
assert_eq!(
out,
Function {
name: String::from("fn"),
args: vec![Expression::from("1.2345 + 5.4321")],
cast: false,
func: true,
}
);
assert_eq!(out, Function::Future(Expression::from("1.2345 + 5.4321")));
}
}

View file

@ -3,13 +3,13 @@ use crate::sql::common::commas;
use crate::sql::idiom::{idiom, Idiom};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::sequence::tuple;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Groups(Vec<Group>);
impl fmt::Display for Groups {
@ -22,7 +22,7 @@ impl fmt::Display for Groups {
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Group {
pub group: Idiom,
}
@ -37,7 +37,7 @@ pub fn group(i: &str) -> IResult<&str, Groups> {
let (i, _) = tag_no_case("GROUP")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("BY"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = separated_nonempty_list(commas, group_raw)(i)?;
let (i, v) = separated_list1(commas, group_raw)(i)?;
Ok((i, Groups(v)))
}

View file

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Ident {
pub name: String,
}

View file

@ -1,16 +1,22 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Idioms(Vec<Idiom>);
impl fmt::Display for Idioms {
@ -20,11 +26,11 @@ impl fmt::Display for Idioms {
}
pub fn idioms(i: &str) -> IResult<&str, Idioms> {
let (i, v) = separated_nonempty_list(commas, idiom)(i)?;
let (i, v) = separated_list1(commas, idiom)(i)?;
Ok((i, Idioms(v)))
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Idiom {
pub parts: Vec<(String, Option<Filter>)>,
}
@ -67,8 +73,19 @@ impl fmt::Display for Idiom {
}
}
impl dbs::Process for Idiom {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn idiom(i: &str) -> IResult<&str, Idiom> {
let (i, v) = separated_nonempty_list(tag("."), all)(i)?;
let (i, v) = separated_list1(tag("."), all)(i)?;
Ok((i, Idiom::from(v)))
}

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Kind {
Any,
Array,

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Limit {
pub expr: u64,
}
@ -23,7 +23,12 @@ 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 {
expr: v,
},
))
}
#[cfg(test)]
@ -37,7 +42,12 @@ 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 {
expr: 100
}
);
assert_eq!("LIMIT 100", format!("{}", out));
}
@ -47,7 +57,12 @@ 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 {
expr: 100
}
);
assert_eq!("LIMIT 100", format!("{}", out));
}
}

View file

@ -1,26 +1,42 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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};
use crate::sql::duration::{duration, Duration};
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::multi::separated_nonempty_list;
use nom::combinator::rest;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Literals(Vec<Literal>);
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 {
@ -28,88 +44,432 @@ impl fmt::Display for Literals {
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
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,
Bool(bool),
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::Null
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(f: f32) -> Self {
Literal::Number(Number::from(f))
fn from(v: f32) -> Self {
Literal::Number(Number::from(v))
}
}
impl From<f64> for Literal {
fn from(f: f64) -> Self {
Literal::Number(Number::from(f))
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(s: &str) -> Self {
Literal::Strand(Strand::from(s))
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::Bool(true) => write!(f, "true"),
Literal::Bool(false) => write!(f, "false"),
Literal::Param(v) => write!(f, "{}", v),
Literal::Idiom(v) => write!(f, "{}", v),
Literal::Thing(v) => write!(f, "{}", v),
Literal::Model(v) => write!(f, "{}", v),
Literal::Array(v) => write!(f, "{}", v),
Literal::Object(v) => write!(f, "{}", v),
Literal::Number(v) => write!(f, "{}", v),
Literal::Strand(v) => write!(f, "{}", v),
Literal::Duration(v) => write!(f, "{}", v),
Literal::Datetime(v) => write!(f, "{}", v),
Literal::Function(v) => write!(f, "{}", v),
Literal::Subquery(v) => write!(f, "{}", v),
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),
}
}
}
pub fn literals(i: &str) -> IResult<&str, Literals> {
let (i, v) = separated_nonempty_list(commas, literal)(i)?;
Ok((i, Literals(v)))
impl dbs::Process for Literal {
fn process(
&self,
ctx: &Parent,
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::Bool(true)),
map(tag_no_case("false"), |_| Literal::Bool(false)),
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(thing, |v| Literal::Thing(v)),
map(model, |v| Literal::Model(v)),
@ -117,11 +477,168 @@ pub fn literal(i: &str) -> IResult<&str, Literal> {
))(i)
}
pub fn simple(i: &str) -> IResult<&str, Literal> {
pub fn what(i: &str) -> IResult<&str, Literal> {
alt((
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(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

@ -8,7 +8,6 @@ pub mod common;
pub mod cond;
pub mod data;
pub mod datetime;
pub mod definition;
pub mod duration;
pub mod expression;
pub mod fetch;
@ -30,6 +29,8 @@ pub mod output;
pub mod param;
pub mod parser;
pub mod permission;
pub mod point;
pub mod polygon;
pub mod query;
pub mod regex;
pub mod split;
@ -41,6 +42,6 @@ pub mod subquery;
pub mod table;
pub mod thing;
pub mod timeout;
pub mod value;
pub mod version;
pub mod view;
pub mod what;

View file

@ -8,7 +8,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Model {
pub table: String,
#[serde(skip_serializing_if = "Option::is_none")]
@ -19,14 +19,20 @@ pub struct Model {
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "|{}:", escape(&self.table, &val_char, "`"))?;
if let Some(ref v) = self.count {
write!(f, "{}", v)?
match self.count {
Some(ref c) => {
let t = escape(&self.table, &val_char, "`");
write!(f, "|{}:{}|", t, c)?;
}
None => {}
}
if let Some(ref v) = self.range {
write!(f, "{}..{}", v.0, v.1)?
match self.range {
Some((ref b, ref e)) => {
let t = escape(&self.table, &val_char, "`");
write!(f, "|{}:{}..{}|", t, b, e)?;
}
None => {}
}
write!(f, "|")?;
Ok(())
}
}

View file

@ -1,37 +1,93 @@
use dec::Decimal;
use nom::number::complete::double;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops;
use std::str::FromStr;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize)]
pub struct Number {
pub value: Decimal,
}
impl Default for Number {
fn default() -> Self {
Number { value: 0i32.into() }
Number {
value: 0i32.into(),
}
}
}
impl From<i8> for Number {
fn from(i: i8) -> Self {
Number {
value: i.into(),
}
}
}
impl From<i16> for Number {
fn from(i: i16) -> Self {
Number {
value: i.into(),
}
}
}
impl From<i32> for Number {
fn from(i: i32) -> Self {
Number { value: i.into() }
Number {
value: i.into(),
}
}
}
impl From<i64> for Number {
fn from(i: i64) -> Self {
Number { value: i.into() }
Number {
value: i.into(),
}
}
}
impl From<u8> for Number {
fn from(i: u8) -> Self {
Number {
value: i.into(),
}
}
}
impl From<u16> for Number {
fn from(i: u16) -> Self {
Number {
value: i.into(),
}
}
}
impl From<u32> for Number {
fn from(i: u32) -> Self {
Number {
value: i.into(),
}
}
}
impl From<u64> for Number {
fn from(i: u64) -> Self {
Number {
value: i.into(),
}
}
}
impl From<f32> for Number {
fn from(f: f32) -> Self {
Number {
value: Decimal::from_str(&f.to_string()).unwrap(),
value: Decimal::from_str(&f.to_string()).unwrap_or(Decimal::new(0, 0)),
}
}
}
@ -39,7 +95,7 @@ impl From<f32> for Number {
impl From<f64> for Number {
fn from(f: f64) -> Self {
Number {
value: Decimal::from_str(&f.to_string()).unwrap(),
value: Decimal::from_str(&f.to_string()).unwrap_or(Decimal::new(0, 0)),
}
}
}
@ -47,7 +103,7 @@ impl From<f64> for Number {
impl<'a> From<&'a str> for Number {
fn from(s: &str) -> Self {
Number {
value: Decimal::from_str(s).unwrap(),
value: Decimal::from_str(s).unwrap_or(Decimal::new(0, 0)),
}
}
}
@ -55,7 +111,15 @@ impl<'a> From<&'a str> for Number {
impl From<String> for Number {
fn from(s: String) -> Self {
Number {
value: Decimal::from_str(&s).unwrap(),
value: Decimal::from_str(&s).unwrap_or(Decimal::new(0, 0)),
}
}
}
impl From<Decimal> for Number {
fn from(v: Decimal) -> Self {
Number {
value: v,
}
}
}
@ -66,6 +130,49 @@ impl fmt::Display for Number {
}
}
impl Serialize for Number {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_some(&self.value)
} else {
let mut val = serializer.serialize_struct("Number", 1)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
impl ops::Add for Number {
type Output = Self;
fn add(self, other: Self) -> Self {
Number::from(self.value + other.value)
}
}
impl ops::Sub for Number {
type Output = Self;
fn sub(self, other: Self) -> Self {
Number::from(self.value - other.value)
}
}
impl ops::Mul for Number {
type Output = Self;
fn mul(self, other: Self) -> Self {
Number::from(self.value * other.value)
}
}
impl ops::Div for Number {
type Output = Self;
fn div(self, other: Self) -> Self {
Number::from(self.value / other.value)
}
}
pub fn number(i: &str) -> IResult<&str, Number> {
let (i, v) = double(i)?;
Ok((i, Number::from(v)))

View file

@ -1,26 +1,39 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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, Expression};
use crate::sql::expression::expression;
use crate::sql::literal::Literal;
use crate::sql::value::Value;
use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::combinator::opt;
use nom::multi::separated_list;
use nom::multi::separated_list0;
use nom::sequence::delimited;
use nom::IResult;
use serde::ser::SerializeMap;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Object(Vec<(String, Expression)>);
const NAME: &'static str = "Object";
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Object {
pub value: Vec<(String, Value)>,
}
impl fmt::Display for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{{ {} }}",
self.0
self.value
.iter()
.map(|(ref k, ref v)| format!("{}: {}", escape(&k, &val_char, "\""), v))
.collect::<Vec<_>>()
@ -29,24 +42,71 @@ impl fmt::Display for Object {
}
}
impl dbs::Process for Object {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
self.value
.iter()
.map(|(k, v)| match v.process(ctx, exe, doc) {
Ok(v) => Ok((k.clone(), Value::from(v))),
Err(e) => Err(e),
})
.collect::<Result<Vec<_>, _>>()
.map(|v| {
Literal::Object(Object {
value: v,
})
})
}
}
impl Serialize for Object {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let mut map = serializer.serialize_map(Some(self.value.len()))?;
for (ref k, ref v) in &self.value {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
map.end()
} else {
let mut val = serializer.serialize_struct(NAME, 1)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
pub fn object(i: &str) -> IResult<&str, Object> {
let (i, _) = tag("{")(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = separated_list(commas, item)(i)?;
let (i, v) = separated_list0(commas, item)(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = opt(tag(","))(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag("}")(i)?;
Ok((i, Object(v)))
Ok((
i,
Object {
value: v,
},
))
}
fn item(i: &str) -> IResult<&str, (String, Expression)> {
fn item(i: &str) -> IResult<&str, (String, Value)> {
let (i, k) = key(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag(":")(i)?;
let (i, _) = mightbespace(i)?;
let (i, v) = expression(i)?;
Ok((i, (String::from(k), v)))
Ok((i, (String::from(k), Value::from(v))))
}
fn key(i: &str) -> IResult<&str, &str> {
@ -77,7 +137,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("{ one: 1, two: 2, tre: 3 }", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
#[test]
@ -87,7 +147,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("{ one: 1, two: 2, tre: 3 }", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
#[test]
@ -97,6 +157,6 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("{ one: 1, two: 2, tre: 3 + 1 }", format!("{}", out));
assert_eq!(out.0.len(), 3);
assert_eq!(out.value.len(), 3);
}
}

View file

@ -6,10 +6,10 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum Operator {
And, // &&
Or, // ||
And, // &&
//
Add, // +
Sub, // -
@ -18,9 +18,6 @@ pub enum Operator {
Inc, // +=
Dec, // -=
//
Exact, // ==
NotExact, // !==
//
Equal, // =
NotEqual, // !=
AllEqual, // *=
@ -58,16 +55,14 @@ impl Default for Operator {
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Operator::And => write!(f, "AND"),
Operator::Or => write!(f, "OR"),
Operator::And => write!(f, "AND"),
Operator::Add => write!(f, "+"),
Operator::Sub => write!(f, "-"),
Operator::Mul => write!(f, "*"),
Operator::Div => write!(f, "/"),
Operator::Inc => write!(f, "+="),
Operator::Dec => write!(f, "-="),
Operator::Exact => write!(f, "=="),
Operator::NotExact => write!(f, "!=="),
Operator::Equal => write!(f, "="),
Operator::NotEqual => write!(f, "!="),
Operator::AllEqual => write!(f, "*="),
@ -119,8 +114,6 @@ pub fn operator(i: &str) -> IResult<&str, Operator> {
map(tag("!="), |_| Operator::NotEqual),
map(tag("*="), |_| Operator::AllEqual),
map(tag("?="), |_| Operator::AnyEqual),
map(tag("=="), |_| Operator::Exact),
map(tag("!=="), |_| Operator::NotExact),
)),
alt((
map(tag("~"), |_| Operator::Like),

View file

@ -4,13 +4,13 @@ use crate::sql::idiom::{idiom, Idiom};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::{map, opt};
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::sequence::tuple;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Orders(Vec<Order>);
impl fmt::Display for Orders {
@ -23,7 +23,7 @@ impl fmt::Display for Orders {
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Order {
pub order: Idiom,
pub random: bool,
@ -56,7 +56,7 @@ pub fn order(i: &str) -> IResult<&str, Orders> {
let (i, _) = tag_no_case("ORDER")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("BY"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = alt((order_rand, separated_nonempty_list(commas, order_raw)))(i)?;
let (i, v) = alt((order_rand, separated_list1(commas, order_raw)))(i)?;
Ok((i, Orders(v)))
}

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Output {
Null,
None,

View file

@ -1,4 +1,10 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::doc::Document;
use crate::err::Error;
use crate::sql::common::val_char;
use crate::sql::literal::Literal;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::IResult;
@ -6,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Param {
pub name: String,
}
@ -25,6 +31,21 @@ impl fmt::Display for Param {
}
}
impl dbs::Process for Param {
fn process(
&self,
ctx: &Parent,
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!()
}
}
pub fn param(i: &str) -> IResult<&str, Param> {
let (i, _) = tag("$")(i)?;
let (i, v) = take_while1(val_char)(i)?;

View file

@ -13,13 +13,13 @@ pub fn parse(input: &str) -> Result<Query, Error> {
Ok(query)
}
}
Err(Err::Error((i, _))) => Err(Error::ParseError {
pos: input.len() - i.len(),
sql: String::from(i),
Err(Err::Error(e)) => Err(Error::ParseError {
pos: input.len() - e.input.len(),
sql: String::from(e.input),
}),
Err(Err::Failure((i, _))) => Err(Error::ParseError {
pos: input.len() - i.len(),
sql: String::from(i),
Err(Err::Failure(e)) => Err(Error::ParseError {
pos: input.len() - e.input.len(),
sql: String::from(e.input),
}),
Err(Err::Incomplete(_)) => Err(Error::EmptyError),
}
@ -78,9 +78,10 @@ mod tests {
test AS `some thing`,
'2012-04-23T18:25:43.511Z' AS utctime,
'2012-04-23T18:25:43.511-08:00' AS pacifictime,
{ key: (3 + 1 + 2), 'some thing': { otherkey: 'text', } } AS object
{ 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 (3 + 3 * 4)=6
AND 3 + 3 * 4 = 6
AND ages CONTAINS 18
@ -94,8 +95,9 @@ mod tests {
let res = parse(sql);
assert!(res.is_ok());
let tmp = res.unwrap();
println!("{:#?}", serde_json::to_string(&tmp).unwrap());
println!("{}", serde_cbor::to_vec(&tmp).unwrap().len());
println!("{}", tmp)
let enc = serde_cbor::to_vec(&tmp).unwrap();
let dec: Query = serde_cbor::from_slice(&enc).unwrap();
assert_eq!(tmp, dec);
}
}

View file

@ -4,12 +4,12 @@ use crate::sql::expression::{expression, Expression};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
use nom::{multi::separated_list, sequence::tuple, IResult};
use nom::{multi::separated_list0, sequence::tuple, IResult};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Permissions {
pub select: Permission,
pub create: Permission,
@ -85,7 +85,7 @@ fn full(i: &str) -> IResult<&str, Permissions> {
}
fn specific(i: &str) -> IResult<&str, Permissions> {
let (i, perms) = separated_list(commas, permission)(i)?;
let (i, perms) = separated_list0(commas, permission)(i)?;
Ok((
i,
Permissions {
@ -129,7 +129,7 @@ fn specific(i: &str) -> IResult<&str, Permissions> {
))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Permission {
None,
Full,
@ -160,7 +160,7 @@ impl fmt::Display for Permission {
fn permission(i: &str) -> IResult<&str, Vec<(Permission, Permission)>> {
let (i, _) = tag_no_case("FOR")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, kind) = separated_list(
let (i, kind) = separated_list0(
commas,
alt((
map(tag_no_case("SELECT"), |_| Permission::Select),

73
src/sql/point.rs Normal file
View file

@ -0,0 +1,73 @@
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));
}
}

77
src/sql/polygon.rs Normal file
View file

@ -0,0 +1,77 @@
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_list0;
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_list0(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) )";
let res = polygon(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("( (0, 0), (0, 0) )", format!("{}", out));
}
#[test]
fn polygon_complex() {
let sql = "( (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) )", format!("{}", out));
}
}

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Query {
statements: Statements,
}
@ -28,7 +28,12 @@ impl fmt::Display for Query {
pub fn query(i: &str) -> IResult<&str, Query> {
let (i, v) = all_consuming(statements)(i)?;
Ok((i, Query { statements: v }))
Ok((
i,
Query {
statements: v,
},
))
}
#[cfg(test)]

View file

@ -3,25 +3,45 @@ use nom::bytes::complete::tag;
use nom::sequence::delimited;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Regex {
pub value: String,
pub input: String,
#[serde(skip)]
pub value: Option<regex::Regex>,
}
impl<'a> From<&'a str> for Regex {
fn from(r: &str) -> Regex {
Regex {
value: String::from(r),
input: String::from(r),
value: match regex::Regex::new(r) {
Ok(v) => Some(v),
Err(_) => None,
},
}
}
}
impl PartialEq for Regex {
fn eq(&self, other: &Self) -> bool {
self.input == other.input
}
}
impl PartialOrd for Regex {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.input.cmp(&other.input))
}
}
impl fmt::Display for Regex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "/{}/", &self.value)
write!(f, "/{}/", &self.input)
}
}

View file

@ -3,13 +3,13 @@ use crate::sql::common::commas;
use crate::sql::idiom::{idiom, Idiom};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::sequence::tuple;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Splits(Vec<Split>);
impl fmt::Display for Splits {
@ -17,16 +17,12 @@ impl fmt::Display for Splits {
write!(
f,
"SPLIT ON {}",
self.0
.iter()
.map(|ref v| format!("{}", v))
.collect::<Vec<_>>()
.join(", ")
self.0.iter().map(|ref v| format!("{}", v)).collect::<Vec<_>>().join(", ")
)
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Split {
pub split: Idiom,
}
@ -41,13 +37,18 @@ pub fn split(i: &str) -> IResult<&str, Splits> {
let (i, _) = tag_no_case("SPLIT")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("ON"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = separated_nonempty_list(commas, split_raw)(i)?;
let (i, v) = separated_list1(commas, split_raw)(i)?;
Ok((i, Splits(v)))
}
fn split_raw(i: &str) -> IResult<&str, Split> {
let (i, v) = idiom(i)?;
Ok((i, Split { split: v }))
Ok((
i,
Split {
split: v,
},
))
}
#[cfg(test)]

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Start {
pub expr: u64,
}
@ -23,7 +23,12 @@ 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 {
expr: v,
},
))
}
#[cfg(test)]
@ -37,7 +42,12 @@ 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 {
expr: 100
}
);
assert_eq!("START 100", format!("{}", out));
}
@ -47,7 +57,12 @@ 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 {
expr: 100
}
);
assert_eq!("START 100", format!("{}", out));
}
}

View file

@ -1,5 +1,11 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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};
@ -23,13 +29,13 @@ use crate::sql::statements::yuse::{yuse, UseStatement};
use nom::branch::alt;
use nom::combinator::map;
use nom::multi::many0;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Statements(pub Vec<Statement>);
impl Statements {
@ -45,12 +51,12 @@ impl fmt::Display for Statements {
}
pub fn statements(i: &str) -> IResult<&str, Statements> {
let (i, v) = separated_nonempty_list(colons, statement)(i)?;
let (i, v) = separated_list1(colons, statement)(i)?;
let (i, _) = many0(alt((colons, comment)))(i)?;
Ok((i, Statements(v)))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Statement {
Set(SetStatement),
Use(UseStatement),
@ -101,29 +107,32 @@ impl fmt::Display for Statement {
}
}
impl Statement {
pub fn execute(&self) -> String {
impl dbs::Process for Statement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
match *self {
Statement::Use(ref v) => format!("{}", v),
Statement::Set(ref v) => format!("{}", v),
Statement::Info(ref v) => format!("{}", v),
Statement::Live(ref v) => format!("{}", v),
Statement::Kill(ref v) => format!("{}", v),
Statement::Begin(ref v) => format!("{}", v),
Statement::Cancel(ref v) => format!("{}", v),
Statement::Commit(ref v) => format!("{}", v),
Statement::Output(ref v) => format!("{}", v),
Statement::Ifelse(ref v) => format!("{}", v),
Statement::Select(ref v) => format!("{}", v),
Statement::Create(ref v) => format!("{}", v),
Statement::Update(ref v) => format!("{}", v),
Statement::Delete(ref v) => format!("{}", v),
Statement::Relate(ref v) => format!("{}", v),
Statement::Insert(ref v) => format!("{}", v),
Statement::Upsert(ref v) => format!("{}", v),
Statement::Define(ref v) => format!("{}", v),
Statement::Remove(ref v) => format!("{}", v),
Statement::Option(ref v) => format!("{}", v),
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),
_ => unreachable!(),
}
}
}

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct BeginStatement;
impl fmt::Display for BeginStatement {

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct CancelStatement;
impl fmt::Display for CancelStatement {

View file

@ -7,7 +7,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct CommitStatement;
impl fmt::Display for CommitStatement {

View file

@ -1,8 +1,14 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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::what::{whats, Whats};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::preceded;
@ -10,9 +16,9 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct CreateStatement {
pub what: Whats,
pub what: Literals,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Data>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -37,6 +43,43 @@ impl fmt::Display for CreateStatement {
}
}
impl dbs::Process for CreateStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
// 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);
}
Literal::Thing(_) => {
i.process_thing(ctx, exe);
}
Literal::Model(_) => {
i.process_model(ctx, exe);
}
Literal::Array(_) => {
i.process_array(ctx, exe);
}
Literal::Object(_) => {
i.process_object(ctx, exe);
}
_ => {
todo!() // Return error
}
};
}
// Output the results
i.output(ctx, exe)
}
}
pub fn create(i: &str) -> IResult<&str, CreateStatement> {
let (i, _) = tag_no_case("CREATE")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,18 +1,22 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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::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::view::{view, View};
use crate::sql::{
duration::{duration, Duration},
kind::{kind, Kind},
permission::{permissions, Permissions},
};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::{map, opt};
@ -20,7 +24,7 @@ use nom::{multi::many0, IResult};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DefineStatement {
Namespace(DefineNamespaceStatement),
Database(DefineDatabaseStatement),
@ -49,6 +53,17 @@ impl fmt::Display for DefineStatement {
}
}
impl dbs::Process for DefineStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn define(i: &str) -> IResult<&str, DefineStatement> {
alt((
map(namespace, |v| DefineStatement::Namespace(v)),
@ -67,7 +82,7 @@ pub fn define(i: &str) -> IResult<&str, DefineStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineNamespaceStatement {
pub name: String,
}
@ -96,7 +111,7 @@ fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineDatabaseStatement {
pub name: String,
}
@ -125,7 +140,7 @@ fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineLoginStatement {
pub name: String,
pub base: Base,
@ -176,7 +191,7 @@ fn login(i: &str) -> IResult<&str, DefineLoginStatement> {
))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DefineLoginOption {
Password(String),
Passhash(String),
@ -206,7 +221,7 @@ fn login_hash(i: &str) -> IResult<&str, DefineLoginOption> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineTokenStatement {
pub name: String,
pub base: Base,
@ -257,7 +272,7 @@ fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineScopeStatement {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
@ -320,7 +335,7 @@ fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DefineScopeOption {
Session(Duration),
Signup(Expression),
@ -368,7 +383,7 @@ fn scope_connect(i: &str) -> IResult<&str, DefineScopeOption> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineTableStatement {
pub name: String,
pub drop: bool,
@ -390,9 +405,6 @@ impl fmt::Display for DefineTableStatement {
if self.full == false {
write!(f, " SCHEMALESS")?
}
if self.drop == true {
write!(f, " DROP")?
}
if let Some(ref v) = self.view {
write!(f, " {}", v)?
}
@ -442,7 +454,7 @@ fn table(i: &str) -> IResult<&str, DefineTableStatement> {
))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DefineTableOption {
Drop,
View(View),
@ -489,7 +501,7 @@ fn table_permissions(i: &str) -> IResult<&str, DefineTableOption> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineEventStatement {
pub name: String,
pub what: String,
@ -540,7 +552,7 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineFieldStatement {
pub name: Idiom,
pub what: String,
@ -615,7 +627,7 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
))
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DefineFieldOption {
Kind(Kind),
Value(Expression),
@ -670,7 +682,7 @@ fn field_permissions(i: &str) -> IResult<&str, DefineFieldOption> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DefineIndexStatement {
pub name: String,
pub what: String,
@ -702,7 +714,7 @@ fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
let (i, _) = tag_no_case("COLUMNS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, cols) = idioms(i)?;
let (i, uniq) = opt(|i: &str| {
let (i, uniq) = opt(|i| {
shouldbespace(i)?;
tag_no_case("UNIQUE")(i)?;
Ok((i, true))

View file

@ -1,8 +1,14 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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::what::{whats, Whats};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::preceded;
@ -11,9 +17,9 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct DeleteStatement {
pub what: Whats,
pub what: Literals,
#[serde(skip_serializing_if = "Option::is_none")]
pub cond: Option<Cond>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -38,6 +44,43 @@ impl fmt::Display for DeleteStatement {
}
}
impl dbs::Process for DeleteStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
// 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);
}
Literal::Thing(_) => {
i.process_thing(ctx, exe);
}
Literal::Model(_) => {
i.process_model(ctx, exe);
}
Literal::Array(_) => {
i.process_array(ctx, exe);
}
Literal::Object(_) => {
i.process_object(ctx, exe);
}
_ => {
todo!() // Return error
}
};
}
// Output the results
i.output(ctx, exe)
}
}
pub fn delete(i: &str) -> IResult<&str, DeleteStatement> {
let (i, _) = tag_no_case("DELETE")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("FROM"))))(i)?;

View file

@ -1,26 +1,36 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::many0;
use nom::multi::separated_list0;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct IfelseStatement {
pub first: (Expression, Expression),
pub other: Vec<(Expression, Expression)>,
pub exprs: Vec<(Expression, Expression)>,
#[serde(skip_serializing_if = "Option::is_none")]
pub close: Option<Expression>,
}
impl fmt::Display for IfelseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IF {} THEN {}", self.first.0, self.first.1)?;
for ref o in self.other.iter() {
write!(f, " ELSE IF {} THEN {}", o.0, o.1)?
}
write!(
f,
"{}",
self.exprs
.iter()
.map(|(ref cond, ref then)| format!("IF {} THEN {}", cond, then))
.collect::<Vec<_>>()
.join(" ELSE ")
)?;
if let Some(ref v) = self.close {
write!(f, " ELSE {}", v)?
}
@ -29,36 +39,42 @@ impl fmt::Display for IfelseStatement {
}
}
impl dbs::Process for IfelseStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, 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);
}
}
match self.close {
Some(ref v) => v.process(ctx, exe, doc),
None => Ok(Literal::None),
}
}
}
pub fn ifelse(i: &str) -> IResult<&str, IfelseStatement> {
let (i, _) = tag_no_case("IF")(i)?;
let (i, first) = first(i)?;
let (i, other) = many0(other)(i)?;
let (i, exprs) = separated_list0(split, exprs)(i)?;
let (i, close) = opt(close)(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("END")(i)?;
Ok((
i,
IfelseStatement {
first,
other,
exprs,
close,
},
))
}
fn first(i: &str) -> IResult<&str, (Expression, Expression)> {
let (i, _) = shouldbespace(i)?;
let (i, cond) = expression(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("THEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, then) = expression(i)?;
Ok((i, (cond, then)))
}
fn other(i: &str) -> IResult<&str, (Expression, Expression)> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ELSE IF")(i)?;
fn exprs(i: &str) -> IResult<&str, (Expression, Expression)> {
let (i, _) = tag_no_case("IF")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, cond) = expression(i)?;
let (i, _) = shouldbespace(i)?;
@ -76,6 +92,13 @@ fn close(i: &str) -> IResult<&str, Expression> {
Ok((i, then))
}
fn split(i: &str) -> IResult<&str, ()> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ELSE")(i)?;
let (i, _) = shouldbespace(i)?;
Ok((i, ()))
}
#[cfg(test)]
mod tests {

View file

@ -1,12 +1,18 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum InfoStatement {
Namespace,
Database,
@ -25,6 +31,17 @@ impl fmt::Display for InfoStatement {
}
}
impl dbs::Process for InfoStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn info(i: &str) -> IResult<&str, InfoStatement> {
let (i, _) = tag_no_case("INFO")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,5 +1,12 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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};
@ -11,7 +18,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct InsertStatement {
pub data: Expression,
pub into: Table,
@ -34,6 +41,32 @@ impl fmt::Display for InsertStatement {
}
}
impl dbs::Process for InsertStatement {
fn process(
&self,
ctx: &Parent,
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 insert(i: &str) -> IResult<&str, InsertStatement> {
let (i, _) = tag_no_case("INSERT")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,11 +1,17 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::bytes::complete::tag_no_case;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct KillStatement {
pub id: Ident,
}
@ -16,9 +22,25 @@ impl fmt::Display for KillStatement {
}
}
impl dbs::Process for KillStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn kill(i: &str) -> IResult<&str, KillStatement> {
let (i, _) = tag_no_case("KILL")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = ident(i)?;
Ok((i, KillStatement { id: v }))
Ok((
i,
KillStatement {
id: v,
},
))
}

View file

@ -1,8 +1,14 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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::what::{whats, Whats};
use crate::sql::literal::Literal;
use crate::sql::literal::{whats, Literals};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::preceded;
@ -10,10 +16,10 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct LiveStatement {
pub expr: Fields,
pub what: Whats,
pub what: Literals,
#[serde(skip_serializing_if = "Option::is_none")]
pub cond: Option<Cond>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -33,6 +39,17 @@ impl fmt::Display for LiveStatement {
}
}
impl dbs::Process for LiveStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn live(i: &str) -> IResult<&str, LiveStatement> {
let (i, _) = tag_no_case("LIVE SELECT")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,6 +1,12 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
@ -10,7 +16,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct OptionStatement {
pub name: Ident,
pub what: bool,
@ -26,19 +32,24 @@ impl fmt::Display for OptionStatement {
}
}
impl dbs::Process for OptionStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn option(i: &str) -> IResult<&str, OptionStatement> {
let (i, _) = tag_no_case("OPTION")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, n) = ident(i)?;
let (i, v) = opt(alt((
map(
tuple((mightbespace, tag("="), mightbespace, tag_no_case("TRUE"))),
|_| true,
),
map(
tuple((mightbespace, tag("="), mightbespace, tag_no_case("FALSE"))),
|_| false,
),
map(tuple((mightbespace, tag("="), mightbespace, tag_no_case("TRUE"))), |_| true),
map(tuple((mightbespace, tag("="), mightbespace, tag_no_case("FALSE"))), |_| false),
)))(i)?;
Ok((
i,

View file

@ -1,11 +1,17 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::bytes::complete::tag_no_case;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct OutputStatement {
pub what: Expression,
}
@ -16,11 +22,27 @@ impl fmt::Display for OutputStatement {
}
}
impl dbs::Process for OutputStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn output(i: &str) -> IResult<&str, OutputStatement> {
let (i, _) = tag_no_case("RETURN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = expression(i)?;
Ok((i, OutputStatement { what: v }))
Ok((
i,
OutputStatement {
what: v,
},
))
}
#[cfg(test)]

View file

@ -1,10 +1,16 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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::what::{whats, Whats};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
@ -15,11 +21,11 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RelateStatement {
pub kind: Table,
pub from: Whats,
pub with: Whats,
pub from: Literals,
pub with: Literals,
pub uniq: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Data>,
@ -48,6 +54,43 @@ impl fmt::Display for RelateStatement {
}
}
impl dbs::Process for RelateStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
// Create a new iterator
let i = Iterator::new();
// 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);
}
Literal::Thing(_) => {
i.process_thing(ctx, exe);
}
Literal::Model(_) => {
i.process_model(ctx, exe);
}
Literal::Array(_) => {
i.process_array(ctx, exe);
}
Literal::Object(_) => {
i.process_object(ctx, exe);
}
_ => {
todo!() // Return error
}
};
}
// Output the results
i.output(ctx, exe)
}
}
pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
let (i, _) = tag_no_case("RELATE")(i)?;
let (i, _) = shouldbespace(i)?;
@ -70,7 +113,7 @@ pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
))
}
fn relate_o(i: &str) -> IResult<&str, (Table, Whats, Whats)> {
fn relate_o(i: &str) -> IResult<&str, (Table, Literals, Literals)> {
let (i, from) = whats(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag("->")(i)?;
@ -83,7 +126,7 @@ fn relate_o(i: &str) -> IResult<&str, (Table, Whats, Whats)> {
Ok((i, (kind, from, with)))
}
fn relate_i(i: &str) -> IResult<&str, (Table, Whats, Whats)> {
fn relate_i(i: &str) -> IResult<&str, (Table, Literals, Literals)> {
let (i, with) = whats(i)?;
let (i, _) = mightbespace(i)?;
let (i, _) = tag("<-")(i)?;

View file

@ -1,6 +1,12 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
@ -8,7 +14,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum RemoveStatement {
Namespace(RemoveNamespaceStatement),
Database(RemoveDatabaseStatement),
@ -37,6 +43,17 @@ impl fmt::Display for RemoveStatement {
}
}
impl dbs::Process for RemoveStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
alt((
map(namespace, |v| RemoveStatement::Namespace(v)),
@ -55,7 +72,7 @@ pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveNamespaceStatement {
pub name: String,
}
@ -84,7 +101,7 @@ fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveDatabaseStatement {
pub name: String,
}
@ -113,7 +130,7 @@ fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveLoginStatement {
pub name: String,
pub base: Base,
@ -148,7 +165,7 @@ fn login(i: &str) -> IResult<&str, RemoveLoginStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveTokenStatement {
pub name: String,
pub base: Base,
@ -183,7 +200,7 @@ fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveScopeStatement {
pub name: String,
}
@ -212,7 +229,7 @@ fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveTableStatement {
pub name: String,
}
@ -241,7 +258,7 @@ fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveEventStatement {
pub name: String,
pub what: String,
@ -276,7 +293,7 @@ fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveFieldStatement {
pub name: String,
pub what: String,
@ -311,7 +328,7 @@ fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoveIndexStatement {
pub name: String,
pub what: String,

View file

@ -1,10 +1,16 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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::group::{group, Groups};
use crate::sql::limit::{limit, Limit};
use crate::sql::literal::{literals, Literals};
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};
@ -17,7 +23,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct SelectStatement {
pub expr: Fields,
pub what: Literals,
@ -75,6 +81,40 @@ impl fmt::Display for SelectStatement {
}
}
impl dbs::Process for SelectStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
// 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);
}
Literal::Thing(_) => {
i.process_thing(ctx, exe);
}
Literal::Model(_) => {
i.process_model(ctx, exe);
}
Literal::Array(_) => {
i.process_array(ctx, exe);
}
_ => {
i.process_query(ctx, exe);
}
};
}
// Output the results
i.output(ctx, exe)
}
}
pub fn select(i: &str) -> IResult<&str, SelectStatement> {
let (i, _) = tag_no_case("SELECT")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,6 +1,12 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
@ -8,7 +14,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct SetStatement {
pub name: Param,
pub what: Expression,
@ -20,6 +26,17 @@ impl fmt::Display for SetStatement {
}
}
impl dbs::Process for SetStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn set(i: &str) -> IResult<&str, SetStatement> {
let (i, _) = tag_no_case("LET")(i)?;
let (i, _) = shouldbespace(i)?;
@ -28,7 +45,13 @@ pub fn set(i: &str) -> IResult<&str, SetStatement> {
let (i, _) = tag("=")(i)?;
let (i, _) = mightbespace(i)?;
let (i, w) = expression(i)?;
Ok((i, SetStatement { name: n, what: w }))
Ok((
i,
SetStatement {
name: n,
what: w,
},
))
}
#[cfg(test)]

View file

@ -1,9 +1,15 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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::what::{whats, Whats};
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::preceded;
@ -11,9 +17,9 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct UpdateStatement {
pub what: Whats,
pub what: Literals,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Data>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -43,6 +49,43 @@ impl fmt::Display for UpdateStatement {
}
}
impl dbs::Process for UpdateStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
// 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);
}
Literal::Thing(_) => {
i.process_thing(ctx, exe);
}
Literal::Model(_) => {
i.process_model(ctx, exe);
}
Literal::Array(_) => {
i.process_array(ctx, exe);
}
Literal::Object(_) => {
i.process_object(ctx, exe);
}
_ => {
todo!() // Return error
}
};
}
// Output the results
i.output(ctx, exe)
}
}
pub fn update(i: &str) -> IResult<&str, UpdateStatement> {
let (i, _) = tag_no_case("UPDATE")(i)?;
let (i, _) = shouldbespace(i)?;

View file

@ -1,5 +1,12 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
use crate::dbs::Iterator;
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};
@ -11,7 +18,7 @@ use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct UpsertStatement {
pub data: Expression,
pub into: Table,
@ -34,6 +41,32 @@ impl fmt::Display for UpsertStatement {
}
}
impl dbs::Process for UpsertStatement {
fn process(
&self,
ctx: &Parent,
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)?;

View file

@ -1,12 +1,18 @@
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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 nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct UseStatement {
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
@ -27,6 +33,17 @@ impl fmt::Display for UseStatement {
}
}
impl dbs::Process for UseStatement {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
todo!()
}
}
pub fn yuse(i: &str) -> IResult<&str, UseStatement> {
alt((both, ns, db))(i)
}

View file

@ -3,15 +3,24 @@ use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::sequence::delimited;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Strand {
pub value: String,
}
impl From<String> for Strand {
fn from(s: String) -> Self {
Strand {
value: s,
}
}
}
impl<'a> From<&'a str> for Strand {
fn from(s: &str) -> Self {
Strand {
@ -26,6 +35,21 @@ impl fmt::Display for Strand {
}
}
impl Serialize for Strand {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_some(&self.value)
} else {
let mut val = serializer.serialize_struct("Strand", 1)?;
val.serialize_field("value", &self.value)?;
val.end()
}
}
}
pub fn strand(i: &str) -> IResult<&str, Strand> {
let (i, v) = strand_raw(i)?;
Ok((i, Strand::from(v)))

View file

@ -1,4 +1,11 @@
use crate::ctx::Context;
use crate::ctx::Parent;
use crate::dbs;
use crate::dbs::Executor;
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};
@ -12,9 +19,10 @@ use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Subquery {
Expression(Expression),
Select(SelectStatement),
@ -27,6 +35,13 @@ pub enum Subquery {
Ifelse(IfelseStatement),
}
impl PartialOrd for Subquery {
#[inline]
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
unreachable!()
}
}
impl fmt::Display for Subquery {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -43,6 +58,83 @@ impl fmt::Display for Subquery {
}
}
impl dbs::Process for Subquery {
fn process(
&self,
ctx: &Parent,
exe: &Executor,
doc: Option<&Document>,
) -> Result<Literal, Error> {
match self {
Subquery::Expression(ref v) => v.process(ctx, exe, doc),
Subquery::Ifelse(ref v) => v.process(ctx, exe, doc),
Subquery::Select(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)
}
Subquery::Create(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)
}
Subquery::Update(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)
}
Subquery::Delete(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)
}
Subquery::Relate(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)
}
Subquery::Insert(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)
}
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)
}
}
}
}
pub fn subquery(i: &str) -> IResult<&str, Subquery> {
alt((subquery_ifelse, subquery_others))(i)
}

View file

@ -2,13 +2,13 @@ use crate::sql::common::commas;
use crate::sql::common::escape;
use crate::sql::common::val_char;
use crate::sql::ident::ident_raw;
use nom::multi::separated_nonempty_list;
use nom::multi::separated_list1;
use nom::IResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Tables(Vec<Table>);
impl fmt::Display for Tables {
@ -18,11 +18,11 @@ impl fmt::Display for Tables {
}
pub fn tables(i: &str) -> IResult<&str, Tables> {
let (i, v) = separated_nonempty_list(commas, table)(i)?;
let (i, v) = separated_list1(commas, table)(i)?;
Ok((i, Tables(v)))
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Table {
pub name: String,
}

View file

@ -3,10 +3,11 @@ use crate::sql::common::val_char;
use crate::sql::ident::ident_raw;
use nom::bytes::complete::tag;
use nom::IResult;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Thing {
pub table: String,
pub id: String,
@ -14,12 +15,26 @@ pub struct Thing {
impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}:{}",
escape(&self.table, &val_char, "`"),
escape(&self.id, &val_char, "`"),
)
let t = escape(&self.table, &val_char, "`");
let i = escape(&self.id, &val_char, "`");
write!(f, "{}:{}", t, i)
}
}
impl Serialize for Thing {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let output = format!("{}:{}", self.table, self.id);
serializer.serialize_some(&output)
} else {
let mut val = serializer.serialize_struct("Thing", 2)?;
val.serialize_field("table", &self.table)?;
val.serialize_field("id", &self.id)?;
val.end()
}
}
}

Some files were not shown because too many files have changed in this diff Show more