#[cfg(any(feature = "cosmos", feature = "gremlin"))]
pub mod gremlin;
#[cfg(feature = "neo4j")]
pub mod neo4j;
use crate::engine::context::{GlobalContext, RequestContext};
use crate::engine::objects::{Node, Rel};
use crate::engine::schema::Info;
use crate::engine::value::Value;
use crate::error::Error;
use async_trait::async_trait;
#[cfg(feature = "neo4j")]
use bb8::Pool;
#[cfg(feature = "neo4j")]
use bb8_bolt::BoltConnectionManager;
#[cfg(any(feature = "cosmos", feature = "gremlin"))]
use gremlin_client::GremlinClient;
use std::collections::HashMap;
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
use std::env::var_os;
use std::fmt::Debug;
#[cfg(feature = "gremlin")]
fn env_bool(var_name: &str) -> Result<bool, Error> {
Ok(env_string(var_name)?.parse::<bool>()?)
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
fn env_string(var_name: &str) -> Result<String, Error> {
var_os(var_name)
.map(|osstr| osstr.to_string_lossy().into_owned())
.ok_or_else(|| Error::EnvironmentVariableNotFound {
name: var_name.to_string(),
})
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
fn env_u16(var_name: &str) -> Result<u16, Error> {
Ok(env_string(var_name)?.parse::<u16>()?)
}
#[derive(Clone, Debug)]
pub enum DatabasePool {
#[cfg(feature = "neo4j")]
Neo4j(Pool<BoltConnectionManager>),
#[cfg(feature = "cosmos")]
Cosmos(GremlinClient),
#[cfg(feature = "gremlin")]
Gremlin((GremlinClient, bool)),
NoDatabase,
}
impl DatabasePool {
#[cfg(feature = "neo4j")]
pub fn neo4j(&self) -> Result<&bb8::Pool<bb8_bolt::BoltConnectionManager>, Error> {
match self {
DatabasePool::Neo4j(pool) => Ok(pool),
_ => Err(Error::DatabaseNotFound {}),
}
}
#[cfg(feature = "cosmos")]
pub fn cosmos(&self) -> Result<&GremlinClient, Error> {
match self {
DatabasePool::Cosmos(pool) => Ok(pool),
_ => Err(Error::DatabaseNotFound {}),
}
}
#[cfg(feature = "gremlin")]
pub fn gremlin(&self) -> Result<&GremlinClient, Error> {
match self {
DatabasePool::Gremlin((pool, _)) => Ok(pool),
_ => Err(Error::DatabaseNotFound {}),
}
}
}
impl Default for DatabasePool {
fn default() -> Self {
DatabasePool::NoDatabase
}
}
#[async_trait]
pub trait DatabaseEndpoint {
async fn pool(&self) -> Result<DatabasePool, Error>;
}
pub(crate) trait Transaction {
fn begin(&mut self) -> Result<(), Error>;
fn node_create_query<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
rel_create_fragments: Vec<String>,
params: HashMap<String, Value>,
node_var: &NodeQueryVar,
props: HashMap<String, Value>,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn create_node<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: HashMap<String, Value>,
partition_key_opt: Option<&Value>,
info: &Info,
) -> Result<Node<GlobalCtx, RequestCtx>, Error>;
fn rel_create_fragment<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
dst_query: &str,
params: HashMap<String, Value>,
rel_var: &RelQueryVar,
props: HashMap<String, Value>,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn rel_create_query<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
src_query_opt: Option<String>,
rel_create_fragments: Vec<String>,
params: HashMap<String, Value>,
rel_vars: Vec<RelQueryVar>,
clause: ClauseType,
) -> Result<(String, HashMap<String, Value>), Error>;
fn create_rels<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: HashMap<String, Value>,
props_type_name: Option<&str>,
partition_key_opt: Option<&Value>,
) -> Result<Vec<Rel<GlobalCtx, RequestCtx>>, Error>;
fn node_read_fragment(
&mut self,
rel_query_fragments: Vec<(String, String)>,
params: HashMap<String, Value>,
node_var: &NodeQueryVar,
props: HashMap<String, Value>,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, String, HashMap<String, Value>), Error>;
fn node_read_query(
&mut self,
match_fragment: &str,
where_fragment: &str,
params: HashMap<String, Value>,
node_var: &NodeQueryVar,
clause: ClauseType,
) -> Result<(String, HashMap<String, Value>), Error>;
fn read_nodes<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: Option<HashMap<String, Value>>,
partition_key_opt: Option<&Value>,
info: &Info,
) -> Result<Vec<Node<GlobalCtx, RequestCtx>>, Error>;
fn rel_read_fragment(
&mut self,
src_query_opt: Option<(String, String)>,
dst_query_opt: Option<(String, String)>,
params: HashMap<String, Value>,
rel_var: &RelQueryVar,
props: HashMap<String, Value>,
sg: &mut SuffixGenerator,
) -> Result<(String, String, HashMap<String, Value>), Error>;
fn rel_read_query(
&mut self,
match_fragment: &str,
where_fragment: &str,
params: HashMap<String, Value>,
rel_var: &RelQueryVar,
clause: ClauseType,
) -> Result<(String, HashMap<String, Value>), Error>;
fn read_rels<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: Option<HashMap<String, Value>>,
props_type_name: Option<&str>,
partition_key_opt: Option<&Value>,
) -> Result<Vec<Rel<GlobalCtx, RequestCtx>>, Error>;
#[allow(clippy::too_many_arguments)]
fn node_update_query<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
match_query: String,
change_queries: Vec<String>,
params: HashMap<String, Value>,
node_var: &NodeQueryVar,
props: HashMap<String, Value>,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn update_nodes<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: HashMap<String, Value>,
partition_key_opt: Option<&Value>,
info: &Info,
) -> Result<Vec<Node<GlobalCtx, RequestCtx>>, Error>;
fn rel_update_query<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
match_query: String,
params: HashMap<String, Value>,
rel_var: &RelQueryVar,
props: HashMap<String, Value>,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn update_rels<GlobalCtx: GlobalContext, RequestCtx: RequestContext>(
&mut self,
query: String,
params: HashMap<String, Value>,
props_type_name: Option<&str>,
partition_key_opt: Option<&Value>,
) -> Result<Vec<Rel<GlobalCtx, RequestCtx>>, Error>;
fn node_delete_query(
&mut self,
match_query: String,
rel_delete_fragments: Vec<String>,
params: HashMap<String, Value>,
node_var: &NodeQueryVar,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn delete_nodes(
&mut self,
query: String,
params: HashMap<String, Value>,
partition_key_opt: Option<&Value>,
) -> Result<i32, Error>;
#[allow(clippy::too_many_arguments)]
fn rel_delete_query(
&mut self,
match_query: String,
src_delete_query_opt: Option<String>,
dst_delete_query_opt: Option<String>,
params: HashMap<String, Value>,
rel_var: &RelQueryVar,
clause: ClauseType,
sg: &mut SuffixGenerator,
) -> Result<(String, HashMap<String, Value>), Error>;
fn delete_rels(
&mut self,
query: String,
params: HashMap<String, Value>,
partition_key_opt: Option<&Value>,
) -> Result<i32, Error>;
fn commit(&mut self) -> Result<(), Error>;
fn rollback(&mut self) -> Result<(), Error>;
}
#[derive(Clone, Debug)]
pub(crate) struct NodeQueryVar {
base: String,
suffix: String,
label: Option<String>,
name: String,
}
impl NodeQueryVar {
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn new(label: Option<String>, base: String, suffix: String) -> NodeQueryVar {
NodeQueryVar {
base: base.clone(),
suffix: suffix.clone(),
label,
name: base + &suffix,
}
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn base(&self) -> &str {
&self.base
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn label(&self) -> Result<&str, Error> {
self.label.as_deref().ok_or_else(|| Error::LabelNotFound)
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn suffix(&self) -> &str {
&self.suffix
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn name(&self) -> &str {
&self.name
}
}
#[derive(Clone, Debug)]
pub(crate) struct RelQueryVar {
label: String,
suffix: String,
name: String,
src: NodeQueryVar,
dst: NodeQueryVar,
}
impl RelQueryVar {
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn new(
label: String,
suffix: String,
src: NodeQueryVar,
dst: NodeQueryVar,
) -> RelQueryVar {
RelQueryVar {
label,
suffix: suffix.clone(),
name: "rel".to_string() + &suffix,
src,
dst,
}
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn label(&self) -> &str {
&self.label
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn name(&self) -> &str {
&self.name
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn src(&self) -> &NodeQueryVar {
&self.src
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn dst(&self) -> &NodeQueryVar {
&self.dst
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum ClauseType {
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
Parameter,
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
FirstSubQuery,
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
SubQuery,
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
Query,
}
#[derive(Default)]
pub(crate) struct SuffixGenerator {
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
seed: i32,
}
impl SuffixGenerator {
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn new() -> SuffixGenerator {
SuffixGenerator { seed: -1 }
}
#[cfg(any(feature = "cosmos", feature = "gremlin", feature = "neo4j"))]
pub(crate) fn suffix(&mut self) -> String {
self.seed += 1;
"_".to_string() + &self.seed.to_string()
}
}