scooby/postgres/tools/
parameters.rs

1use crate::tools::{build_array, joined};
2
3/// Generator of PostgreSQL parameter placeholders for dynamic statements with multiple values
4///
5/// # Example
6///
7/// ```
8/// use scooby::postgres::Parameters;
9///
10/// let mut params = Parameters::new();
11/// let p1 = params.next();
12/// let p2 = params.next();
13/// let p345 = params.next_n(3);
14/// let p67 = params.next_array::<2>();
15///
16/// assert_eq!(p1, "$1");
17/// assert_eq!(p2, "$2");
18/// assert_eq!(p345, "$3, $4, $5");
19/// assert_eq!(p67, ["$6", "$7"]);
20/// ```
21pub struct Parameters {
22    current: usize,
23}
24
25impl Parameters {
26    /// Make a new Parameters counter, starting with 1
27    pub fn new() -> Parameters {
28        Parameters { current: 1 }
29    }
30
31    /// Make a new Parameters counter, starting with passed number
32    pub fn starting_from(first: usize) -> Parameters {
33        Parameters { current: first }
34    }
35
36    /// Return the current parameter placeholder in `$x` format, and increase the internal counter
37    pub fn next(&mut self) -> String {
38        let s = format!("${}", self.current);
39        self.current += 1;
40        s
41    }
42
43    /// Return N next placeholders in `$x, $y, $z` format
44    pub fn next_n(&mut self, n: usize) -> String {
45        let last = self.current + n;
46        // TODO: This allocates a bunch of strings totally unnecessarily
47        let s = joined((self.current..last).map(|x| format!("${}", x)), ", ").to_string();
48        self.current = last;
49        s
50    }
51
52    /// Return N next placeholders as an array of size N
53    pub fn next_array<const N: usize>(&mut self) -> [String; N] {
54        build_array(|| self.next())
55    }
56}
57
58impl Default for Parameters {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn next_once() {
70        let mut params = Parameters::new();
71        assert_eq!(params.next(), "$1");
72    }
73
74    #[test]
75    fn next_twice() {
76        let mut params = Parameters::new();
77        params.next();
78        assert_eq!(params.next(), "$2");
79    }
80
81    #[test]
82    fn next_n_once() {
83        let mut params = Parameters::new();
84        assert_eq!(params.next_n(5), "$1, $2, $3, $4, $5");
85    }
86
87    #[test]
88    fn next_n_with_one() {
89        let mut params = Parameters::new();
90        assert_eq!(params.next_n(1), "$1");
91    }
92
93    #[test]
94    fn next_n_twice() {
95        let mut params = Parameters::new();
96        params.next_n(3);
97        assert_eq!(params.next_n(3), "$4, $5, $6");
98    }
99
100    #[test]
101    fn next_arr() {
102        let mut params = Parameters::new();
103        let p = params.next_array::<5>();
104        assert_eq!(p, ["$1", "$2", "$3", "$4", "$5"]);
105    }
106
107    #[test]
108    fn next_arr_twice() {
109        let mut params = Parameters::new();
110        let p1 = params.next_array::<2>();
111        let p2 = params.next_array::<3>();
112        assert_eq!(p1, ["$1", "$2"]);
113        assert_eq!(p2, ["$3", "$4", "$5"]);
114    }
115}