use crate::arg::SqlArg;
use std::collections::HashMap;
pub trait Bind {
fn bind(&self, arg: &dyn SqlArg) -> String;
fn binds(&self, args: &[&dyn SqlArg]) -> String;
fn bind_num(&self, num: u16, arg: &dyn SqlArg) -> String;
fn bind_nums(&self, args: &[&dyn SqlArg]) -> String;
fn bind_name(&self, name: &dyn ToString, arg: &dyn SqlArg) -> String;
fn bind_names(&self, names: &dyn BindNames) -> String;
}
impl Bind for &str {
fn bind(&self, arg: &dyn SqlArg) -> String {
(*self).to_string().bind(arg)
}
fn binds(&self, args: &[&dyn SqlArg]) -> String {
(*self).to_string().binds(args)
}
fn bind_num(&self, num: u16, arg: &dyn SqlArg) -> String {
(*self).to_string().bind_num(num, arg)
}
fn bind_nums(&self, args: &[&dyn SqlArg]) -> String {
(*self).to_string().bind_nums(args)
}
fn bind_name(&self, name: &dyn ToString, arg: &dyn SqlArg) -> String {
(*self).to_string().bind_name(name, arg)
}
fn bind_names<'a>(&self, names: &dyn BindNames) -> String {
(*self).to_string().bind_names(names)
}
}
impl Bind for String {
fn bind(&self, arg: &dyn SqlArg) -> String {
self.replacen('?', &arg.sql_arg(), 1)
}
fn binds(&self, args: &[&dyn SqlArg]) -> String {
let mut offset = 0;
let mut res = String::new();
let len = args.len();
for ch in self.chars() {
if ch == '?' {
res.push_str(&args[offset].sql_arg());
offset = (offset + 1) % len;
} else {
res.push(ch);
}
}
res
}
fn bind_num(&self, num: u16, arg: &dyn SqlArg) -> String {
let rep = format!("${}", &num);
self.replace(&rep, &arg.sql_arg())
}
fn bind_nums(&self, args: &[&dyn SqlArg]) -> String {
let mut res = String::new();
let mut num = 0usize;
let mut wait_digit = false;
let len = args.len();
for ch in self.chars() {
if ch == '$' {
if wait_digit {
if num > 0 {
let idx = num - 1;
if len > idx {
res.push_str(&args[idx].sql_arg());
}
num = 0;
} else {
wait_digit = false;
res.push(ch);
}
} else {
wait_digit = true;
}
} else if wait_digit {
if let Some(digit) = ch.to_digit(10) {
num = num * 10 + digit as usize;
} else {
let idx = num - 1;
if len > idx {
res.push_str(&args[idx].sql_arg());
}
res.push(ch);
wait_digit = false;
num = 0;
}
} else {
res.push(ch);
}
}
if wait_digit && num > 0 {
let idx = num - 1;
if len > idx {
res.push_str(&args[idx].sql_arg());
}
}
res
}
fn bind_name(&self, name: &dyn ToString, arg: &dyn SqlArg) -> String {
let rep = format!(":{}:", &name.to_string());
self.replace(&rep, &arg.sql_arg())
}
fn bind_names<'a>(&self, names: &dyn BindNames) -> String {
let mut res = String::new();
let mut key = String::new();
let mut wait_colon = false;
let names = names.names_map();
for ch in self.chars() {
if ch == ':' {
if wait_colon {
if key.is_empty() {
res.push(ch);
} else {
let skey = key.to_string();
if let Some(value) = names.get(&*skey) {
res.push_str(&value.sql_arg());
} else {
res.push_str("NULL");
}
key = String::new();
}
wait_colon = false;
} else {
wait_colon = true;
}
} else if wait_colon {
key.push(ch);
} else {
res.push(ch);
}
}
if wait_colon {
res.push(';');
res.push_str(&key);
}
res
}
}
pub trait BindNames<'a> {
fn names_map(&self) -> HashMap<&'a str, &dyn SqlArg>;
}
impl<'a> BindNames<'a> for HashMap<&'a str, &dyn SqlArg> {
fn names_map(&self) -> HashMap<&'a str, &dyn SqlArg> {
self.to_owned()
}
}
impl<'a> BindNames<'a> for &HashMap<&'a str, &dyn SqlArg> {
fn names_map(&self) -> HashMap<&'a str, &dyn SqlArg> {
self.to_owned().to_owned()
}
}
impl<'a> BindNames<'a> for Vec<(&'a str, &dyn SqlArg)> {
fn names_map(&self) -> HashMap<&'a str, &dyn SqlArg> {
let mut map = HashMap::new();
for (k, v) in self.iter() {
map.insert(*k, *v);
}
map
}
}
impl<'a> BindNames<'a> for &[(&'a str, &dyn SqlArg)] {
fn names_map(&self) -> HashMap<&'a str, &dyn SqlArg> {
let mut map = HashMap::new();
for (k, v) in self.iter() {
map.insert(*k, *v);
}
map
}
}