nb_to_query/
lib.rs

1#![feature(iter_intersperse)]
2
3use chrono::{DateTime, NaiveDate, TimeZone};
4pub use nb_to_query_derive::ToQueryDerive;
5
6pub trait ToQuery {
7    fn to_query(&self, field_name: &str) -> Option<String>;
8}
9
10impl ToQuery for String {
11    fn to_query(&self, field_name: &str) -> Option<String> {
12        Some(format!("{}={}", field_name, self))
13    }
14}
15
16impl ToQuery for &str {
17    fn to_query(&self, field_name: &str) -> Option<String> {
18        Some(format!("{}={}", field_name, self))
19    }
20}
21
22impl ToQuery for bool {
23    fn to_query(&self, field_name: &str) -> Option<String> {
24        Some(format!("{}={}", field_name, self))
25    }
26}
27
28impl ToQuery for i8 {
29    fn to_query(&self, field_name: &str) -> Option<String> {
30        Some(format!("{}={}", field_name, self))
31    }
32}
33
34impl ToQuery for i16 {
35    fn to_query(&self, field_name: &str) -> Option<String> {
36        Some(format!("{}={}", field_name, self))
37    }
38}
39
40impl ToQuery for i32 {
41    fn to_query(&self, field_name: &str) -> Option<String> {
42        Some(format!("{}={}", field_name, self))
43    }
44}
45
46impl ToQuery for i64 {
47    fn to_query(&self, field_name: &str) -> Option<String> {
48        Some(format!("{}={}", field_name, self))
49    }
50}
51
52impl ToQuery for i128 {
53    fn to_query(&self, field_name: &str) -> Option<String> {
54        Some(format!("{}={}", field_name, self))
55    }
56}
57
58impl ToQuery for u8 {
59    fn to_query(&self, field_name: &str) -> Option<String> {
60        Some(format!("{}={}", field_name, self))
61    }
62}
63
64impl ToQuery for u16 {
65    fn to_query(&self, field_name: &str) -> Option<String> {
66        Some(format!("{}={}", field_name, self))
67    }
68}
69
70impl ToQuery for u32 {
71    fn to_query(&self, field_name: &str) -> Option<String> {
72        Some(format!("{}={}", field_name, self))
73    }
74}
75
76impl ToQuery for u64 {
77    fn to_query(&self, field_name: &str) -> Option<String> {
78        Some(format!("{}={}", field_name, self))
79    }
80}
81
82impl ToQuery for u128 {
83    fn to_query(&self, field_name: &str) -> Option<String> {
84        Some(format!("{}={}", field_name, self))
85    }
86}
87
88impl ToQuery for f32 {
89    fn to_query(&self, field_name: &str) -> Option<String> {
90        Some(format!("{}={}", field_name, self))
91    }
92}
93
94impl ToQuery for f64 {
95    fn to_query(&self, field_name: &str) -> Option<String> {
96        Some(format!("{}={}", field_name, self))
97    }
98}
99
100impl<T> ToQuery for Vec<T>
101where
102    T: ToQuery,
103{
104    fn to_query(&self, field_name: &str) -> Option<String> {
105        if self.is_empty() {
106            return None;
107        }
108        let pairs: String = self
109            .iter()
110            .flat_map(|v| v.to_query(field_name))
111            .intersperse("&".into())
112            .collect();
113        if pairs.is_empty() {
114            return None;
115        }
116        Some(pairs)
117    }
118}
119
120impl<T> ToQuery for Option<T>
121where
122    T: ToQuery,
123{
124    fn to_query(&self, field_name: &str) -> Option<String> {
125        self.as_ref().map(|v| v.to_query(field_name))?
126    }
127}
128
129#[cfg(feature = "chrono")]
130impl<TZ> ToQuery for DateTime<TZ>
131where
132    TZ: TimeZone,
133{
134    fn to_query(&self, field_name: &str) -> Option<String> {
135        Some(format!("{}={}", field_name, self.to_rfc3339()))
136    }
137}
138
139#[cfg(feature = "chrono")]
140impl ToQuery for NaiveDate {
141    fn to_query(&self, field_name: &str) -> Option<String> {
142        Some(format!("{}={}", field_name, self.format("%Y-%m-%d")))
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use chrono::TimeZone;
150
151    #[derive(ToQueryDerive)]
152    struct TestStruct {
153        a: String,
154        b: bool,
155        c: i32,
156        d: Vec<String>,
157    }
158    #[test]
159    fn test_to_query() {
160        let test_struct = TestStruct {
161            a: "a".to_string(),
162            b: true,
163            c: 1,
164            d: vec!["a".to_string(), "b".to_string()],
165        };
166        let query = test_struct.to_query("");
167        assert_eq!(query, Some("a=a&b=true&c=1&d=a&d=b".into()));
168    }
169
170    #[cfg(feature = "chrono")]
171    #[derive(ToQueryDerive)]
172    struct TestStruct2<TZ>
173    where
174        TZ: TimeZone,
175    {
176        a: DateTime<TZ>,
177    }
178
179    #[cfg(feature = "chrono")]
180    #[test]
181    fn test_to_query2() {
182        use chrono::Utc;
183
184        let test_struct = TestStruct2::<Utc> {
185            a: Utc.with_ymd_and_hms(2023, 12, 3, 11, 45, 0).unwrap(),
186        };
187        let query = test_struct.to_query("");
188        assert_eq!(query, Some("a=2023-12-03T11:45:00+00:00".into()));
189    }
190}