use std::collections::BTreeMap;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::{self, Value};
use serde_json::error::Error as JsonError;
#[macro_export]
macro_rules! cypher_stmt {
( $s:expr ) => {{
Ok($crate::Statement::new($s)) as Result<$crate::Statement, $crate::serde_json::error::Error>
}};
( $s:expr, { $( $k:expr => $v:expr ),+ } ) => {{
let mut stmt = $crate::Statement::new($s);
let mut error: Option<$crate::serde_json::error::Error> = None;
$(
if error.is_none() {
match stmt.add_param($k, $v) {
Err(e) => error = Some(e),
_ => {}
}
}
)*
if let Some(error) = error {
Err($crate::error::GraphError::Serde(error))
} else {
Ok(stmt)
}
}}
}
#[derive(Clone, Debug, Serialize)]
pub struct Statement {
statement: String,
parameters: BTreeMap<String, Value>,
}
impl Statement {
pub fn new<T: Into<String>>(statement: T) -> Self {
Statement {
statement: statement.into(),
parameters: BTreeMap::new(),
}
}
pub fn statement(&self) -> &str {
&self.statement
}
pub fn add_param<K, V>(&mut self, key: K, value: V) -> Result<(), JsonError>
where K: Into<String>, V: Serialize + Copy
{
self.parameters.insert(key.into(), serde_json::value::to_value(&value)?);
Ok(())
}
pub fn with_param<K, V>(mut self, key: K, value: V) -> Result<Self, JsonError>
where K: Into<String>, V: Serialize + Copy
{
self.add_param(key, value)?;
Ok(self)
}
pub fn param<T: DeserializeOwned>(&self, key: &str) -> Option<Result<T, serde_json::error::Error>> {
self.parameters.get(key).map(|v| serde_json::value::from_value(v.clone()))
}
pub fn parameters(&self) -> &BTreeMap<String, Value> {
&self.parameters
}
pub fn set_parameters<T: Serialize>(&mut self, params: &BTreeMap<String, T>)
-> Result<(), JsonError>
{
let mut parameters = BTreeMap::new();
for (k, v) in params {
parameters.insert(k.to_owned(), serde_json::value::to_value(v)?);
}
self.parameters = parameters;
Ok(())
}
pub fn remove_param(&mut self, key: &str) {
self.parameters.remove(key);
}
}
impl<T: Into<String>> From<T> for Statement {
fn from(stmt: T) -> Self {
Statement::new(stmt)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(unused_variables)]
fn from_str() {
let stmt = Statement::new("MATCH n RETURN n");
}
#[test]
fn with_param() {
let statement = Statement::new("MATCH n RETURN n")
.with_param("param1", "value1").unwrap()
.with_param("param2", 2).unwrap()
.with_param("param3", 3.0).unwrap()
.with_param("param4", [0; 4]).unwrap();
assert_eq!(statement.parameters().len(), 4);
}
#[test]
fn add_param() {
let mut statement = Statement::new("MATCH n RETURN n");
statement.add_param("param1", "value1").unwrap();
statement.add_param("param2", 2).unwrap();
statement.add_param("param3", 3.0).unwrap();
statement.add_param("param4", [0; 4]).unwrap();
assert_eq!(statement.parameters().len(), 4);
}
#[test]
fn remove_param() {
let mut statement = Statement::new("MATCH n RETURN n")
.with_param("param1", "value1").unwrap()
.with_param("param2", 2).unwrap()
.with_param("param3", 3.0).unwrap()
.with_param("param4", [0; 4]).unwrap();
statement.remove_param("param1");
assert_eq!(statement.parameters().len(), 3);
}
#[test]
#[allow(unused_variables)]
fn macro_without_params() {
let stmt = cypher_stmt!("MATCH n RETURN n").unwrap();
}
#[test]
fn macro_single_param() {
let statement1 = cypher_stmt!("MATCH n RETURN n", {
"name" => "test"
}).unwrap();
let param = 1;
let statement2 = cypher_stmt!("MATCH n RETURN n", {
"value" => param
}).unwrap();
assert_eq!("test", statement1.param::<String>("name").unwrap().unwrap());
assert_eq!(param, statement2.param::<i32>("value").unwrap().unwrap());
}
#[test]
fn macro_multiple_params() {
let param = 3f32;
let statement = cypher_stmt!("MATCH n RETURN n", {
"param1" => "one",
"param2" => 2,
"param3" => param
}).unwrap();
assert_eq!("one", statement.param::<String>("param1").unwrap().unwrap());
assert_eq!(2, statement.param::<i32>("param2").unwrap().unwrap());
assert_eq!(param, statement.param::<f32>("param3").unwrap().unwrap());
}
}