sqlx_askama_template/
lib.rs

1#![doc = include_str!("../README.md")]
2use sqlx::{Arguments, Database, Execute};
3pub use sqlx_askama_template_macro::*;
4use std::cell::RefCell;
5
6/// Internal executor for SQL templates
7pub struct SqlTemplateExecute<'q, DB: Database> {
8    /// Reference to SQL query string
9    pub(crate) sql: &'q str,
10    /// SQL parameters
11    pub(crate) arguments: Option<DB::Arguments<'q>>,
12    /// Persistent flag
13    pub(crate) persistent: bool,
14}
15impl<DB: Database> SqlTemplateExecute<'_, DB> {
16    pub fn set_persistent(&mut self, persistent: bool) {
17        self.persistent = persistent;
18    }
19}
20impl<'q, DB: Database> Execute<'q, DB> for SqlTemplateExecute<'q, DB> {
21    /// Returns the SQL query string
22    fn sql(&self) -> &'q str {
23        self.sql
24    }
25
26    /// Gets prepared statement (not supported in this implementation)
27    fn statement(&self) -> Option<&DB::Statement<'q>> {
28        None
29    }
30
31    /// Takes ownership of the bound arguments
32    fn take_arguments(&mut self) -> Result<Option<DB::Arguments<'q>>, sqlx::error::BoxDynError> {
33        Ok(self.arguments.take())
34    }
35
36    /// Checks if query is persistent
37    fn persistent(&self) -> bool {
38        self.persistent
39    }
40}
41
42/// SQL template argument processor
43///
44/// Handles parameter encoding and binding for SQL templates
45pub struct TemplateArg<'q, DB: Database> {
46    /// Stores any encoding errors
47    error: RefCell<Option<sqlx::error::BoxDynError>>,
48    /// Stores SQL parameters
49    arguments: RefCell<Option<DB::Arguments<'q>>>,
50}
51
52impl<DB: Database> Default for TemplateArg<'_, DB> {
53    /// Creates default TemplateArg
54    fn default() -> Self {
55        TemplateArg {
56            error: RefCell::new(None),
57            arguments: RefCell::new(None),
58        }
59    }
60}
61
62impl<'q, DB: Database> TemplateArg<'q, DB> {
63    /// Encodes a single parameter and returns its placeholder
64    ///
65    /// # Arguments
66    /// * `t` - Value to encode
67    ///
68    /// # Returns
69    /// Parameter placeholder string (e.g. "$1" or "?")
70    pub fn encode<T>(&self, t: T) -> String
71    where
72        T: sqlx::Encode<'q, DB> + sqlx::Type<DB> + 'q,
73    {
74        let mut err = self.error.borrow_mut();
75        let mut arguments = self.arguments.borrow_mut().take().unwrap_or_default();
76
77        if let Err(e) = arguments.add(t) {
78            if err.is_none() {
79                *err = Some(e);
80            }
81        }
82
83        let mut placeholder = String::new();
84        if let Err(e) = arguments.format_placeholder(&mut placeholder) {
85            if err.is_none() {
86                *err = Some(Box::new(e));
87            }
88        }
89
90        *self.arguments.borrow_mut() = Some(arguments);
91        placeholder
92    }
93
94    /// Encodes a parameter list and returns placeholder sequence
95    ///
96    /// # Arguments
97    /// * `args` - Iterator of values to encode
98    ///
99    /// # Returns
100    /// Parameter placeholder sequence (e.g. "($1,$2,$3)")
101    pub fn encode_list<T>(&self, args: impl Iterator<Item = T>) -> String
102    where
103        T: sqlx::Encode<'q, DB> + sqlx::Type<DB> + 'q,
104    {
105        let mut err = self.error.borrow_mut();
106        let mut arguments = self.arguments.borrow_mut().take().unwrap_or_default();
107        let mut placeholder = String::new();
108        placeholder.push('(');
109
110        for arg in args {
111            if let Err(e) = arguments.add(arg) {
112                if err.is_none() {
113                    *err = Some(e);
114                }
115            }
116
117            if let Err(e) = arguments.format_placeholder(&mut placeholder) {
118                if err.is_none() {
119                    *err = Some(Box::new(e));
120                }
121            }
122            placeholder.push(',');
123        }
124
125        if placeholder.ends_with(",") {
126            placeholder.pop();
127        }
128        placeholder.push(')');
129
130        *self.arguments.borrow_mut() = Some(arguments);
131        placeholder
132    }
133
134    /// Takes any encoding error that occurred
135    pub fn get_err(&self) -> Option<sqlx::error::BoxDynError> {
136        self.error.borrow_mut().take()
137    }
138
139    /// Takes ownership of the encoded arguments
140    pub fn get_arguments(&self) -> Option<DB::Arguments<'q>> {
141        self.arguments.borrow_mut().take()
142    }
143}
144
145/// SQL template trait
146///
147/// Defines basic operations for rendering SQL from templates
148pub trait SqlTemplate<'q, DB>: Sized
149where
150    DB: Database,
151{
152    /// Renders SQL template and returns query string with parameters
153    fn render_sql(self) -> Result<(String, Option<DB::Arguments<'q>>), askama::Error>;
154
155    /// Renders SQL template and returns executable query result
156    fn render_execute_able(
157        self,
158        sql_buffer: &'q mut String,
159    ) -> Result<SqlTemplateExecute<'q, DB>, askama::Error> {
160        let (sql, arguments) = self.render_sql()?;
161        *sql_buffer = sql;
162        Ok(SqlTemplateExecute {
163            sql: sql_buffer,
164
165            arguments,
166
167            persistent: true,
168        })
169    }
170}