sql_support/
repeat.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::fmt;
6
7/// Helper type for printing repeated strings more efficiently. You should use
8/// [`repeat_display`] or one of the `repeat_sql_*` helpers to
9/// construct it.
10#[derive(Debug, Clone)]
11pub struct RepeatDisplay<'a, F> {
12    count: usize,
13    sep: &'a str,
14    fmt_one: F,
15}
16
17impl<F> fmt::Display for RepeatDisplay<'_, F>
18where
19    F: Fn(usize, &mut fmt::Formatter<'_>) -> fmt::Result,
20{
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        for i in 0..self.count {
23            if i != 0 {
24                f.write_str(self.sep)?;
25            }
26            (self.fmt_one)(i, f)?;
27        }
28        Ok(())
29    }
30}
31
32/// Construct a RepeatDisplay that will repeatedly call `fmt_one` with a formatter `count` times,
33/// separated by `sep`.
34///
35/// # Example
36///
37/// ```rust
38/// # use sql_support::repeat_display;
39/// assert_eq!(format!("{}", repeat_display(1, ",", |i, f| write!(f, "({},?)", i))),
40///            "(0,?)");
41/// assert_eq!(format!("{}", repeat_display(2, ",", |i, f| write!(f, "({},?)", i))),
42///            "(0,?),(1,?)");
43/// assert_eq!(format!("{}", repeat_display(3, ",", |i, f| write!(f, "({},?)", i))),
44///            "(0,?),(1,?),(2,?)");
45/// ```
46#[inline]
47pub fn repeat_display<F>(count: usize, sep: &str, fmt_one: F) -> RepeatDisplay<'_, F>
48where
49    F: Fn(usize, &mut fmt::Formatter<'_>) -> fmt::Result,
50{
51    RepeatDisplay {
52        count,
53        sep,
54        fmt_one,
55    }
56}
57
58/// Returns a value that formats as `count` instances of `?` separated by commas.
59///
60/// # Example
61///
62/// ```rust
63/// # use sql_support::repeat_sql_vars;
64/// assert_eq!(format!("{}", repeat_sql_vars(0)), "");
65/// assert_eq!(format!("{}", repeat_sql_vars(1)), "?");
66/// assert_eq!(format!("{}", repeat_sql_vars(2)), "?,?");
67/// assert_eq!(format!("{}", repeat_sql_vars(3)), "?,?,?");
68/// ```
69pub fn repeat_sql_vars(count: usize) -> impl fmt::Display {
70    repeat_display(count, ",", |_, f| write!(f, "?"))
71}
72
73/// Returns a value that formats as `count` instances of `(?)` separated by commas.
74///
75/// # Example
76///
77/// ```rust
78/// # use sql_support::repeat_sql_values;
79/// assert_eq!(format!("{}", repeat_sql_values(0)), "");
80/// assert_eq!(format!("{}", repeat_sql_values(1)), "(?)");
81/// assert_eq!(format!("{}", repeat_sql_values(2)), "(?),(?)");
82/// assert_eq!(format!("{}", repeat_sql_values(3)), "(?),(?),(?)");
83/// ```
84///
85pub fn repeat_sql_values(count: usize) -> impl fmt::Display {
86    // We could also implement this as `repeat_sql_multi_values(count, 1)`,
87    // but this is faster and no less clear IMO.
88    repeat_display(count, ",", |_, f| write!(f, "(?)"))
89}
90
91/// Returns a value that formats as `num_values` instances of `(?,?,?,...)` (where there are
92/// `vars_per_value` question marks separated by commas in between the `?`s).
93///
94/// Panics if `vars_per_value` is zero (however, `num_values` is allowed to be zero).
95///
96/// # Example
97///
98/// ```rust
99/// # use sql_support::repeat_multi_values;
100/// assert_eq!(format!("{}", repeat_multi_values(0, 2)), "");
101/// assert_eq!(format!("{}", repeat_multi_values(1, 5)), "(?,?,?,?,?)");
102/// assert_eq!(format!("{}", repeat_multi_values(2, 3)), "(?,?,?),(?,?,?)");
103/// assert_eq!(format!("{}", repeat_multi_values(3, 1)), "(?),(?),(?)");
104/// ```
105pub fn repeat_multi_values(num_values: usize, vars_per_value: usize) -> impl fmt::Display {
106    assert_ne!(
107        vars_per_value, 0,
108        "Illegal value for `vars_per_value`, must not be zero"
109    );
110    repeat_display(num_values, ",", move |_, f| {
111        write!(f, "({})", repeat_sql_vars(vars_per_value))
112    })
113}