use crate::error::{BindError, DriverError, Result};
use lazy_static::lazy_static;
use regex::{Captures, Regex};
use serde::Serialize;
use serde_json::{Map, Value};
use std::collections::HashMap;
type BindResult = std::result::Result<String, BindError>;
pub fn bind<T>(query: &str, params: T) -> Result<String>
where
T: Serialize,
{
Ok(serde_json::to_value(params)
.map_err(BindError::DeserializeError)
.and_then(|val| parametrize_query(query, val))
.map_err(DriverError::BindError)?)
}
#[inline]
fn parametrize_query(query: &str, val: Value) -> BindResult {
match val {
Value::Object(o) => bind_map_params(query, gen_map_params(o)),
Value::Array(a) => bind_seq_params(query, gen_seq_params(a)),
_ => Err(BindError::SerializeError),
}
}
fn bind_map_params(query: &str, map: HashMap<String, String>) -> BindResult {
lazy_static! {
static ref RE: Regex = Regex::new(r"[:\w\\]:\w+|:\w+:|(:(\w+))").unwrap();
}
let dummy = ""; let mut result = Ok(dummy.to_owned());
let q = RE.replace_all(query, |cap: &Captures| match map.get(&cap[2]) {
Some(k) => k,
None => {
result = Err(BindError::MappingError(cap[1].to_owned()));
dummy
}
});
result.and(Ok(q.into_owned()))
}
fn bind_seq_params(query: &str, arr: Vec<String>) -> BindResult {
lazy_static! {
static ref RE: Regex = Regex::new(r"[:\d\\]:\d+|:\d+:|(:(\d+))").unwrap();
}
let dummy = "".to_owned(); let mut res1 = Ok(&dummy); let mut res2 = Ok(0usize);
let q = RE.replace_all(query, |cap: &Captures| {
let index = cap[2].parse();
match index {
Ok(i) => match arr.get(i) {
Some(k) => k,
None => {
res1 = Err(BindError::MappingError(cap[1].to_owned()));
&dummy
}
},
Err(_) => {
res2 = index.map_err(BindError::ParseIntError);
&dummy
}
}
});
res1.and(res2).and(Ok(q.into_owned()))
}
#[inline]
fn gen_map_params(params: Map<String, Value>) -> HashMap<String, String> {
params
.into_iter()
.map(|(k, v)| (k, into_sql_param(v)))
.collect()
}
#[inline]
fn gen_seq_params(params: Vec<Value>) -> Vec<String> {
params.into_iter().map(into_sql_param).collect()
}
fn into_sql_param(val: Value) -> String {
match val {
Value::Null => "NULL".to_owned(),
Value::String(s) => format!("'{}'", s.replace('\'', "''")),
Value::Number(n) => n.to_string(),
Value::Bool(b) => match b {
true => "1".to_owned(),
false => "0".to_owned(),
},
Value::Array(a) => {
let vec = a
.into_iter()
.map(into_sql_param)
.collect::<Vec<String>>()
.join(", ");
format!("({})", vec)
}
Value::Object(o) => {
let vec = o
.into_iter()
.map(|(_, v)| into_sql_param(v))
.collect::<Vec<String>>()
.join(", ");
format!("({})", vec)
}
}
}