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}