use {
std::collections::HashMap,
syn::{parse_str, Ident},
};
#[derive(Clone, Copy, Debug)]
pub enum QueryType {
PostgreSQL,
SQLite,
}
pub fn parse_query(typ: QueryType, query: impl AsRef<str>) -> (String, HashMap<Ident, usize>) {
#[derive(Clone, Copy, Debug)]
enum State {
Query,
Ident,
}
let query = query.as_ref();
let mut postgres = String::with_capacity(query.len());
let mut sqlite = String::with_capacity(query.len());
let mut idents = HashMap::new();
let mut state = State::Query;
let mut temp = String::with_capacity(10);
for c in query.chars() {
match c {
'{' => match state {
State::Query => {
state = State::Ident;
}
State::Ident => {
panic!("provided sql is invalid");
}
},
'}' => match state {
State::Query => {
panic!("provided sql is invalid");
}
State::Ident => {
state = State::Query;
let ident = temp.clone();
temp.clear();
let ident = parse_str(&ident).expect("ident not found");
let next_index = idents.len() + 1;
let index = *idents.entry(ident).or_insert(next_index);
match typ {
QueryType::PostgreSQL => {
postgres.push('$');
postgres.push_str(&index.to_string());
}
QueryType::SQLite => {
sqlite.push('?');
sqlite.push_str(&index.to_string());
}
}
}
},
_ => match state {
State::Query => {
push(typ, &mut postgres, &mut sqlite, c);
}
State::Ident => {
temp.push(c);
}
},
}
}
(
match typ {
QueryType::PostgreSQL => postgres,
QueryType::SQLite => sqlite,
},
idents,
)
}
fn push(typ: QueryType, postgres: &mut String, sqlite: &mut String, c: char) {
match typ {
QueryType::PostgreSQL => {
postgres.push(c);
}
QueryType::SQLite => {
sqlite.push(c);
}
}
}
#[cfg(test)]
mod test {
use super::{parse_query, QueryType};
const GET_BY_ID: &str = "SELECT Id, Name FROM Tag WHERE Id = {id};";
fn postgres_parse(query: impl AsRef<str>) -> String {
parse_query(QueryType::PostgreSQL, query).0
}
fn sqlite_parse(query: impl AsRef<str>) -> String {
parse_query(QueryType::SQLite, query).0
}
#[test]
fn postgres_get_by_id() {
let query = postgres_parse(&GET_BY_ID);
assert_eq!("SELECT Id, Name FROM Tag WHERE Id = $1;", query);
}
#[test]
fn sqlite_get_by_id() {
let query = sqlite_parse(&GET_BY_ID);
assert_eq!("SELECT Id, Name FROM Tag WHERE Id = ?1;", query);
}
}