twilight_http/
query_formatter.rs

1use std::fmt::{Display, Formatter, Write};
2
3/// A helper struct to write query paramseters to a formatter.
4pub struct QueryStringFormatter<'w1, 'w2> {
5    formatter: &'w1 mut Formatter<'w2>,
6    is_first: bool,
7}
8
9impl<'w1, 'w2> QueryStringFormatter<'w1, 'w2> {
10    pub fn new(formatter: &'w1 mut Formatter<'w2>) -> Self {
11        Self {
12            formatter,
13            is_first: true,
14        }
15    }
16
17    /// Writes a query parameter to the formatter.
18    ///
19    /// # Errors
20    ///
21    /// This returns a [`std::fmt::Error`] if the formatter returns an error.
22    pub fn write_param(&mut self, key: &str, value: &impl Display) -> std::fmt::Result {
23        if self.is_first {
24            self.formatter.write_char('?')?;
25            self.is_first = false;
26        } else {
27            self.formatter.write_char('&')?;
28        }
29
30        self.formatter.write_str(key)?;
31        self.formatter.write_char('=')?;
32        Display::fmt(value, self.formatter)
33    }
34
35    /// Writes a query parameter to the formatter.
36    ///
37    /// # Errors
38    ///
39    /// This returns a [`std::fmt::Error`] if the formatter returns an error.
40    pub fn write_opt_param(&mut self, key: &str, value: Option<&impl Display>) -> std::fmt::Result {
41        if let Some(value) = value {
42            self.write_param(key, value)
43        } else {
44            Ok(())
45        }
46    }
47}
48
49/// Provides a display implementation for serializing iterable objects into
50/// query params.
51#[derive(Debug)]
52pub struct QueryArray<T>(pub T);
53
54impl<T, U> Display for QueryArray<T>
55where
56    T: IntoIterator<Item = U> + Clone,
57    U: Display,
58{
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        let mut iter = self.0.clone().into_iter().peekable();
61
62        while let Some(item) = iter.next() {
63            Display::fmt(&item, f)?;
64            if iter.peek().is_some() {
65                f.write_str(",")?;
66            }
67        }
68
69        Ok(())
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    struct Test {
78        a: Option<u32>,
79        b: Option<String>,
80    }
81
82    impl Display for Test {
83        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84            let mut writer = QueryStringFormatter::new(f);
85            writer.write_opt_param("a", self.a.as_ref())?;
86            writer.write_opt_param("b", self.b.as_ref())
87        }
88    }
89
90    #[test]
91    fn test_query_string_formatter_filled() {
92        let test = Test {
93            a: Some(1),
94            b: Some("hello".to_string()),
95        };
96
97        assert_eq!(test.to_string(), "?a=1&b=hello");
98    }
99
100    #[test]
101    fn test_query_string_formatter_empty() {
102        let test = Test { a: None, b: None };
103
104        assert_eq!(test.to_string(), "");
105    }
106
107    #[test]
108    fn test_query_string_formatter_single() {
109        let test = Test {
110            a: Some(1),
111            b: None,
112        };
113
114        assert_eq!(test.to_string(), "?a=1");
115    }
116
117    #[test]
118    fn test_query_array() {
119        let query_array = QueryArray([1, 2, 3]);
120        assert_eq!(query_array.to_string(), "1,2,3");
121
122        let params = vec!["a", "b", "c"];
123        let query_array = QueryArray(&params);
124        assert_eq!(query_array.to_string(), "a,b,c");
125    }
126}