use anyhow::Result;
use serde_json::Value;
use std::str::Chars;
pub fn build_insert_placeholders<'a>(values: &[&'a Value]) -> (Vec<String>, Vec<&'a Value>) {
let mut placeholders: Vec<String> = Vec::with_capacity(values.len());
let mut bind_values: Vec<&Value> = Vec::new();
let mut next_param_index = 1;
for value in values {
if value.is_null() {
placeholders.push("NULL".to_string());
} else {
placeholders.push(format!("${}", next_param_index));
bind_values.push(value);
next_param_index += 1;
}
}
(placeholders, bind_values)
}
#[derive(Debug)]
pub struct Condition {
pub column: String,
pub value: Value,
}
impl Condition {
pub fn eq(column: impl Into<String>, value: impl Into<Value>) -> Self {
Self {
column: column.into(),
value: value.into(),
}
}
}
pub fn sanitize_identifier(identifier: &str) -> Option<String> {
let mut chars: Chars<'_> = identifier.chars();
let first: char = chars.next()?;
if !(first.is_ascii_alphabetic() || first == '_') {
return None;
}
if !chars.all(|c| c.is_ascii_alphanumeric() || c == '_') {
return None;
}
Some(format!("\"{}\"", identifier))
}
pub fn build_where_clause(
conditions: &[Condition],
start_index: usize,
) -> Result<(String, Vec<Value>)> {
let mut clause: String = String::new();
let mut values: Vec<Value> = Vec::new();
let mut idx: usize = start_index;
for condition in conditions {
if let Some(column) = sanitize_identifier(&condition.column) {
if clause.is_empty() {
clause.push_str(" WHERE ");
} else {
clause.push_str(" AND ");
}
clause.push_str(&format!("t.{} = ${}", column, idx));
idx += 1;
values.push(condition.value.clone());
}
}
Ok((clause, values))
}