sqlx_askama_template/v3/
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/// - `'q`: 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<'q>>>,
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 `'q`
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<ImplEncode>(&self, t: ImplEncode) -> String
53    where
54        ImplEncode: Encode<'q, DB> + Type<DB> + 'q,
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<ImplEncode>(&self, args: impl ::std::iter::IntoIterator<Item = ImplEncode>) -> String
87    where
88        ImplEncode: Encode<'q, DB> + Type<DB> + 'q,
89    {
90        let mut placeholder = String::new();
91        placeholder.push('(');
92
93        for arg in args {
94            placeholder.push_str(&self.e(arg));
95
96            placeholder.push(',');
97        }
98
99        if placeholder.ends_with(",") {
100            placeholder.pop();
101        }
102        placeholder.push(')');
103
104        placeholder
105    }
106    /// Clone-and-encode variant for types requiring cloning
107    ///
108    /// # Arguments
109    /// * `t` - Reference to clonable encodable value
110    pub fn et<ImplEncode>(&self, t: &ImplEncode) -> ::std::string::String
111    where
112        ImplEncode: Encode<'q, DB> + Type<DB> + ::std::clone::Clone + 'q,
113    {
114        self.e(t.clone())
115    }
116    /// Clone-and-encode list variant
117    ///
118    /// # Arguments
119    /// * `args` - Iterator of references to clonable values
120    pub fn etl<'arg_b, ImplEncode>(
121        &self,
122        args: impl ::std::iter::IntoIterator<Item = &'arg_b ImplEncode>,
123    ) -> ::std::string::String
124    where
125        'q: 'arg_b,
126        ImplEncode: Encode<'q, DB> + Type<DB> + ::std::clone::Clone + 'q,
127    {
128        let args = args.into_iter().cloned();
129        self.el(args)
130    }
131
132    /// Takes any encoding error that occurred
133    pub fn get_err(&self) -> Option<Error> {
134        self.error.borrow_mut().take()
135    }
136
137    /// Takes ownership of the encoded arguments
138    pub fn get_arguments(&self) -> Option<DB::Arguments<'q>> {
139        self.arguments.borrow_mut().take()
140    }
141}
142
143impl<'q, DB: Database, D> Deref for TemplateArg<'q, DB, D> {
144    type Target = &'q D;
145    fn deref(&self) -> &Self::Target {
146        &self.data
147    }
148}