Skip to main content

a3s_orm/query/
raw.rs

1use std::marker::PhantomData;
2
3use crate::{CompiledQuery, Dialect, Error, Query, Result, Value};
4
5#[derive(Clone, Debug)]
6enum SqlPart {
7    Trusted(&'static str),
8    Value(Value),
9}
10
11/// A trusted static SQL query with separately bound values.
12///
13/// Text parts must have a static lifetime. Runtime values can only enter the
14/// query through bind, which emits a dialect-specific placeholder.
15#[derive(Clone, Debug)]
16pub struct SqlQuery<O> {
17    parts: Vec<SqlPart>,
18    marker: PhantomData<fn() -> O>,
19}
20
21pub fn sql_query<O>(sql: &'static str) -> SqlQuery<O> {
22    SqlQuery {
23        parts: vec![SqlPart::Trusted(sql)],
24        marker: PhantomData,
25    }
26}
27
28impl<O> SqlQuery<O> {
29    pub fn append(mut self, sql: &'static str) -> Self {
30        self.parts.push(SqlPart::Trusted(sql));
31        self
32    }
33
34    pub fn bind(mut self, value: impl Into<Value>) -> Self {
35        self.parts.push(SqlPart::Value(value.into()));
36        self
37    }
38}
39
40impl<O> Query for SqlQuery<O> {
41    type Output = O;
42
43    fn compile(self, dialect: &impl Dialect) -> Result<CompiledQuery> {
44        let mut sql = String::new();
45        let mut parameters = Vec::new();
46        for part in self.parts {
47            match part {
48                SqlPart::Trusted(part) => sql.push_str(part),
49                SqlPart::Value(value) => {
50                    parameters.push(value);
51                    sql.push_str(&dialect.placeholder(parameters.len()));
52                }
53            }
54        }
55        if sql.trim().is_empty() {
56            return Err(Error::EmptyRawQuery);
57        }
58        Ok(CompiledQuery { sql, parameters })
59    }
60}