querylib 0.5.0

Rust query language
Documentation
use std::fmt::Display;

use crate::query::*;

#[derive(Clone, Debug, PartialEq)]
pub struct Param { value: Value }
impl Param {
    pub fn from_value(v: Value) -> Param {
        Param { value: v }
    }
}
pub trait ToParam {
  fn to_param(self: Self, params: &mut Vec<Param>) -> String;
}

impl ToParam for Value {
  fn to_param(self, params: &mut Vec<Param>) -> String { 
    params.push(Param::from_value(self));
    format!("${}", params.len())
  }
}

#[allow(dead_code)]
pub struct Where { 
    where_clause: String,
    params: Vec<Param>,
}

impl Display for Where {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.where_clause)
    }
}

impl Where {
    fn from(wc: String, params: Vec<Param>) -> Where {
        Where { where_clause: wc, params }
    }
}
pub trait ToWhere {
    fn to_where(&self) -> Where {
        self.to_where_with_params(&mut vec![])
    }
    fn to_where_with_params(&self, params: &mut Vec<Param>) -> Where;
}

impl ToWhere for Query {
  fn to_where_with_params(&self, params: &mut Vec<Param>) -> Where {
    match self {
      Query::And { left, right } => Where::from(format!("({} AND {})", left.to_where_with_params(params), right.to_where_with_params(params)), params.clone()),
      Query::Or { left, right } => Where::from(format!("({} OR {})", left.to_where_with_params(params), right.to_where_with_params(params)), params.clone()),
      Query::Eq { field, value } => Where::from(format!("{field} = {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::Neq { field, value } => Where::from(format!("{field} != {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::Gt { field, value } => Where::from(format!("{field} > {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::GtE { field, value } => Where::from(format!("{field} >= {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::Lt { field, value } => Where::from(format!("{field} < {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::LtE { field, value } => Where::from(format!("{field} <= {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::Rx { field, value } => Where::from(format!("{field} LIKE {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::In { field, value } => Where::from(format!("{field} IN {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::Contains { field, value } => Where::from(format!("{field} CONTAINS {value}", field = field, value = value.clone().to_param(params)), params.clone()),
      Query::None => Where::from("".to_owned(), params.clone()),
    }
  }
}

pub fn to_where(query: &dyn ToWhere) -> Where {
    query.to_where()
}  

#[cfg(test)]
mod test {
  use crate::postgres::{self, *};

  #[test]
  fn test_postgress() {
    let q = query!("deleted" == false && "b" == 5);
    let q2 = query!(..q.clone(); || "c" == 7);
    let q3 = query!(..q.clone(); && ("a" == 5 || "b" < 5));
    let q_r = "(deleted = $1 AND b = $2)".to_owned();
    let q2_r = format!("({} OR c = $3)", q_r);
    let q3_r = format!("((deleted = $1 AND b = $2) AND (a = $3 OR b < $4))");
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(5))]);
    assert_eq!(postgres::to_where(&q2).where_clause, q2_r);
    assert_eq!(postgres::to_where(&q3).where_clause, q3_r);
  }

  #[test]
  fn query_in_bson_string() {
    let q = query!("deleted" == false && "b" in ["5","6","7"]);
    let q_r = "(deleted = $1 AND b IN $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(vec!["5","6","7"]))])
  }

  #[test]
  fn query_in_bson_i32() {
    let q = query!("deleted" == false && "b" in [5,6,7]);
    let q_r = "(deleted = $1 AND b IN $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(vec![5,6,7]))])
  }

  #[test]
  fn query_in_bson_i64() {
    let q = query!("deleted" == false && "b" in [5i64,6i64,7i64]);
    let q_r = "(deleted = $1 AND b IN $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(vec![5i64,6i64,7i64]))])
  }

  #[test]
  fn query_in_bson_f64() {
    let q = query!("deleted" == false && "b" in [5.5f64,6.3f64,7f64]);
    let q_r = "(deleted = $1 AND b IN $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(vec![5.5f64,6.3f64,7f64]))])
  }

  #[test]
  fn query_contains_string() {
    let q = query!("deleted" == false && "b" contains "hi");
    let q_r = "(deleted = $1 AND b CONTAINS $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from("hi"))])
  }

  #[test]
  fn query_contains_integer() {
    let q = query!("deleted" == false && "b" contains 6);
    let q_r = "(deleted = $1 AND b CONTAINS $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(6))])
  }

  #[test]
  fn query_contains_float() {
    let q = query!("deleted" == false && "b" contains 123.43f64);
    let q_r = "(deleted = $1 AND b CONTAINS $2)".to_owned();
    let result = postgres::to_where(&q);
    assert_eq!(result.where_clause, q_r);
    assert_eq!(result.params, vec![Param::from_value(Value::from(false)), Param::from_value(Value::from(123.43f64))])
  }
}