1use std::collections::HashMap;
34
35use pylon_value::DecodedValue;
36
37pub trait QueryArg {
41 fn to_decoded(&self) -> DecodedValue;
42}
43
44impl<T: QueryArg + ?Sized> QueryArg for &T {
45 fn to_decoded(&self) -> DecodedValue {
46 (**self).to_decoded()
47 }
48}
49
50impl QueryArg for DecodedValue {
51 fn to_decoded(&self) -> DecodedValue {
52 self.clone()
53 }
54}
55
56impl<T: QueryArg> QueryArg for Option<T> {
58 fn to_decoded(&self) -> DecodedValue {
59 match self {
60 Some(value) => value.to_decoded(),
61 None => DecodedValue::Null,
62 }
63 }
64}
65
66macro_rules! query_arg_via_into {
67 ($($target:ty),* $(,)?) => {
68 $(
69 impl QueryArg for $target {
70 fn to_decoded(&self) -> DecodedValue {
71 DecodedValue::from(self.clone())
72 }
73 }
74 )*
75 };
76}
77
78query_arg_via_into!(bool, i16, i32, i64, f32, f64, String, uuid::Uuid);
79
80impl QueryArg for str {
81 fn to_decoded(&self) -> DecodedValue {
82 DecodedValue::Str(self.to_string())
83 }
84}
85
86impl QueryArg for Vec<u8> {
93 fn to_decoded(&self) -> DecodedValue {
94 DecodedValue::Bytes(self.clone())
95 }
96}
97
98macro_rules! query_arg_array {
99 ($($element:ty),* $(,)?) => {
100 $(
101 impl QueryArg for Vec<$element> {
102 fn to_decoded(&self) -> DecodedValue {
103 DecodedValue::Array(self.iter().map(QueryArg::to_decoded).collect())
104 }
105 }
106
107 impl QueryArg for [$element] {
108 fn to_decoded(&self) -> DecodedValue {
109 DecodedValue::Array(self.iter().map(QueryArg::to_decoded).collect())
110 }
111 }
112 )*
113 };
114}
115
116query_arg_array!(bool, i16, i32, i64, f32, f64, String, uuid::Uuid);
117
118impl QueryArg for chrono::DateTime<chrono::Utc> {
121 fn to_decoded(&self) -> DecodedValue {
122 DecodedValue::Timestamptz(pg_micros(self.naive_utc()))
123 }
124}
125
126impl QueryArg for chrono::NaiveDateTime {
128 fn to_decoded(&self) -> DecodedValue {
129 DecodedValue::Timestamp(pg_micros(*self))
130 }
131}
132
133impl QueryArg for chrono::NaiveDate {
135 fn to_decoded(&self) -> DecodedValue {
136 DecodedValue::Date(self.signed_duration_since(pg_epoch_date()).num_days() as i32)
137 }
138}
139
140impl QueryArg for chrono::NaiveTime {
142 fn to_decoded(&self) -> DecodedValue {
143 let midnight = chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("00:00:00 is a valid time");
144 DecodedValue::Time(
145 self.signed_duration_since(midnight)
146 .num_microseconds()
147 .unwrap_or_default(),
148 )
149 }
150}
151
152impl QueryArg for std::time::Duration {
159 fn to_decoded(&self) -> DecodedValue {
160 DecodedValue::Interval {
161 months: 0,
162 days: 0,
163 microseconds: i64::try_from(self.as_micros()).unwrap_or(i64::MAX),
164 }
165 }
166}
167
168impl QueryArg for serde_json::Value {
174 fn to_decoded(&self) -> DecodedValue {
175 DecodedValue::Str(self.to_string())
176 }
177}
178
179fn pg_epoch_date() -> chrono::NaiveDate {
180 chrono::NaiveDate::from_ymd_opt(2000, 1, 1).expect("2000-01-01 is a valid date")
181}
182
183fn pg_micros(value: chrono::NaiveDateTime) -> i64 {
188 let epoch = pg_epoch_date().and_hms_opt(0, 0, 0).expect("00:00:00 is a valid time");
189 value
190 .signed_duration_since(epoch)
191 .num_microseconds()
192 .unwrap_or(i64::MAX)
193}
194
195#[derive(Debug, Clone, PartialEq)]
199pub struct ValueOpt(DecodedValue);
200
201impl<T: QueryArg> From<T> for ValueOpt {
202 fn from(value: T) -> Self {
203 ValueOpt(value.to_decoded())
204 }
205}
206
207impl ValueOpt {
208 pub fn into_decoded(self) -> DecodedValue {
209 self.0
210 }
211}
212
213impl From<ValueOpt> for DecodedValue {
214 fn from(value: ValueOpt) -> Self {
215 value.0
216 }
217}
218
219pub trait QueryArgs {
223 fn to_params(&self) -> Vec<(&str, DecodedValue)>;
226}
227
228impl QueryArgs for () {
229 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
230 Vec::new()
231 }
232}
233
234impl QueryArgs for [(&str, DecodedValue)] {
235 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
236 self.iter().map(|(name, value)| (*name, value.clone())).collect()
237 }
238}
239
240impl<const N: usize> QueryArgs for [(&str, DecodedValue); N] {
241 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
242 self.as_slice().to_params()
243 }
244}
245
246impl QueryArgs for Vec<(&str, DecodedValue)> {
247 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
248 self.as_slice().to_params()
249 }
250}
251
252impl QueryArgs for HashMap<&str, ValueOpt> {
257 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
258 self.iter().map(|(name, value)| (*name, value.0.clone())).collect()
259 }
260}
261
262impl QueryArgs for HashMap<String, ValueOpt> {
263 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
264 self.iter()
265 .map(|(name, value)| (name.as_str(), value.0.clone()))
266 .collect()
267 }
268}
269
270const POSITIONAL_NAMES: [&str; 12] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"];
273
274macro_rules! impl_query_args_for_tuple {
275 ($($index:tt : $param:ident),+) => {
276 impl<$($param: QueryArg),+> QueryArgs for ($($param,)+) {
277 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
278 vec![$((POSITIONAL_NAMES[$index], self.$index.to_decoded())),+]
279 }
280 }
281 };
282}
283
284impl_query_args_for_tuple!(0: A);
285impl_query_args_for_tuple!(0: A, 1: B);
286impl_query_args_for_tuple!(0: A, 1: B, 2: C);
287impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D);
288impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
289impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
290impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
291impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
292impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
293impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
294impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K);
295impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L);
296
297#[macro_export]
309macro_rules! named_args {
310 ($($key:expr => $value:expr,)+) => { $crate::named_args!($($key => $value),+) };
311 ($($key:expr => $value:expr),*) => {{
312 let mut args = ::std::collections::HashMap::<&str, $crate::ValueOpt>::new();
313 $(
314 args.insert($key, $crate::ValueOpt::from($value));
315 )*
316 args
317 }};
318}