use crate::builder::build_query;
use regex::Regex;
use sqlx::mysql::MySqlArguments;
use sqlx::query::Query;
use sqlx::{mysql::MySqlQueryResult, Executor, MySql};
pub type Q<'q> = Query<'q, MySql, MySqlArguments>;
pub struct PreparedQuery<F> {
sql: String,
order: Vec<String>,
binder: F,
}
impl<F> PreparedQuery<F>
where
F: for<'q> FnMut(Q<'q>, &str) -> Q<'q>,
{
pub fn new<T>(template: T, binder: F) -> crate::Result<Self>
where
T: Into<String>,
{
let template = template.into();
let order = Regex::new(r":[a-zA-Z0-9_]+")?
.find_iter(&template)
.map(|m| m.as_str().to_owned())
.collect();
let sql = build_query(&template)?;
Ok(Self { sql, order, binder })
}
pub async fn execute<'e, E>(&mut self, executor: E) -> crate::Result<MySqlQueryResult>
where
E: Executor<'e, Database = MySql>,
{
let &mut PreparedQuery {
ref sql,
ref order,
ref mut binder,
} = self;
let mut q = sqlx::query::<MySql>(sql);
for key in order.iter() {
q = binder(q, key);
}
Ok(q.execute(executor).await?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prepared_query_new() {
let result = PreparedQuery::new(
"SELECT * FROM users WHERE id = :id",
|q, _| q,
);
assert!(result.is_ok());
}
#[test]
fn test_prepared_query_placeholder_order() {
let query = PreparedQuery::new(
"SELECT * FROM users WHERE id = :id AND name = :name",
|q, _| q,
).unwrap();
assert_eq!(query.order, vec![":id", ":name"]);
assert_eq!(query.sql, "SELECT * FROM users WHERE id = ? AND name = ?");
}
#[test]
fn test_prepared_query_repeated_placeholders() {
let query = PreparedQuery::new(
"SELECT * FROM users WHERE id = :id OR user_id = :id",
|q, _| q,
).unwrap();
assert_eq!(query.order, vec![":id", ":id"]);
assert_eq!(query.sql, "SELECT * FROM users WHERE id = ? OR user_id = ?");
}
}