Skip to main content

sqlx_askama_template/
template_arg.rs

1use std::{cell::RefCell, ops::Deref};
2
3use sqlx_core::{Error, arguments::Arguments, database::Database, encode::Encode, types::Type};
4/// SQL template argument processor handling safe parameter encoding and placeholder generation
5///
6/// # Generic Parameters
7/// - `'t`: Lifetime for database arguments
8/// - `DB`: Database type implementing [`sqlx::Database`]
9/// - `D`: Template data type
10pub struct TemplateArg<'q, DB: Database, D> {
11    /// Stores any encoding errors
12    error: RefCell<Option<Error>>,
13    /// Stores SQL parameters
14    arguments: RefCell<Option<DB::Arguments>>,
15    encode_placeholder_fn: Option<fn(usize, &mut String)>,
16    data: &'q D,
17}
18
19impl<'q, DB: Database, D> TemplateArg<'q, DB, D> {
20    /// Creates a new TemplateArg instance wrapping template data
21    ///
22    /// # Arguments
23    /// * `d` - Reference to template data with lifetime `'t`
24    pub fn new(d: &'q D) -> Self {
25        TemplateArg {
26            error: RefCell::new(None),
27            arguments: RefCell::new(None),
28            encode_placeholder_fn: None,
29            data: d,
30        }
31    }
32    /// Sets custom placeholder formatting function
33    ///
34    /// # Arguments
35    /// * `f` - Function that takes parameter index and appends placeholder
36    pub fn set_encode_placeholder_fn(&mut self, f: fn(usize, &mut String)) {
37        self.encode_placeholder_fn = Some(f);
38    }
39
40    /// Encodes a single parameter and returns its placeholder
41    ///
42    /// # Arguments
43    /// * `t` - Value implementing [`sqlx::Encode`] and [`sqlx::Type`]
44    ///
45    /// # Returns
46    /// Placeholder string (e.g., `$1` or `?`)
47    ///
48    /// # Example
49    /// ```
50    /// let placeholder = arg.e(user_id);
51    /// ```
52    pub fn e<'t, ImplEncode>(&self, t: ImplEncode) -> String
53    where
54        ImplEncode: Encode<'t, DB> + Type<DB>,
55    {
56        let mut arguments = self.arguments.borrow_mut().take().unwrap_or_default();
57        let mut err = self.error.borrow_mut();
58
59        if let Err(encode_err) = arguments.add(t)
60            && err.is_none()
61        {
62            *err = Some(Error::Encode(encode_err));
63        }
64
65        let mut placeholder = String::new();
66        if let Some(encode_placeholder_fn) = &self.encode_placeholder_fn {
67            encode_placeholder_fn(arguments.len(), &mut placeholder);
68        } else if let Err(e) = arguments.format_placeholder(&mut placeholder) {
69            *err = Some(Error::Encode(Box::new(e)));
70        }
71        *self.arguments.borrow_mut() = Some(arguments);
72        placeholder
73    }
74    /// Encodes an iterable of parameters and returns parenthesized placeholders
75    ///
76    /// # Arguments
77    /// * `args` - Iterator of encodable values
78    ///
79    /// # Returns
80    /// Comma-separated placeholders wrapped in parentheses
81    ///
82    /// # Example
83    /// ```
84    /// let placeholders = arg.el(&[1, 2, 3]);
85    /// ```
86    pub fn el<'t, ImplEncode>(
87        &self,
88        args: impl ::std::iter::IntoIterator<Item = ImplEncode>,
89    ) -> String
90    where
91        ImplEncode: Encode<'t, DB> + Type<DB>,
92    {
93        let mut placeholder = String::new();
94        placeholder.push('(');
95
96        for arg in args {
97            placeholder.push_str(&self.e(arg));
98
99            placeholder.push(',');
100        }
101
102        if placeholder.ends_with(",") {
103            placeholder.pop();
104        }
105        placeholder.push(')');
106
107        placeholder
108    }
109
110    /// Takes any encoding error that occurred
111    pub fn get_err(&self) -> Option<Error> {
112        self.error.borrow_mut().take()
113    }
114
115    /// Takes ownership of the encoded arguments
116    pub fn get_arguments(&self) -> Option<DB::Arguments> {
117        self.arguments.borrow_mut().take()
118    }
119}
120
121impl<'t, DB: Database, D> Deref for TemplateArg<'t, DB, D> {
122    type Target = &'t D;
123    fn deref(&self) -> &Self::Target {
124        &self.data
125    }
126}