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)]
200pub struct ValueOpt(DecodedValue);
201
202impl<T: QueryArg> From<T> for ValueOpt {
203 fn from(value: T) -> Self {
204 ValueOpt(value.to_decoded())
205 }
206}
207
208impl ValueOpt {
209 pub fn into_decoded(self) -> DecodedValue {
210 self.0
211 }
212}
213
214impl From<ValueOpt> for DecodedValue {
215 fn from(value: ValueOpt) -> Self {
216 value.0
217 }
218}
219
220pub trait QueryArgs {
224 fn to_params(&self) -> Vec<(&str, DecodedValue)>;
227}
228
229impl QueryArgs for () {
230 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
231 Vec::new()
232 }
233}
234
235impl QueryArgs for [(&str, DecodedValue)] {
236 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
237 self.iter().map(|(name, value)| (*name, value.clone())).collect()
238 }
239}
240
241impl<const N: usize> QueryArgs for [(&str, DecodedValue); N] {
242 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
243 self.as_slice().to_params()
244 }
245}
246
247impl QueryArgs for Vec<(&str, DecodedValue)> {
248 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
249 self.as_slice().to_params()
250 }
251}
252
253impl QueryArgs for HashMap<&str, ValueOpt> {
259 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
260 self.iter().map(|(name, value)| (*name, value.0.clone())).collect()
261 }
262}
263
264impl QueryArgs for HashMap<String, ValueOpt> {
265 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
266 self.iter()
267 .map(|(name, value)| (name.as_str(), value.0.clone()))
268 .collect()
269 }
270}
271
272const POSITIONAL_NAMES: [&str; 12] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"];
275
276macro_rules! impl_query_args_for_tuple {
277 ($($index:tt : $param:ident),+) => {
278 impl<$($param: QueryArg),+> QueryArgs for ($($param,)+) {
279 fn to_params(&self) -> Vec<(&str, DecodedValue)> {
280 vec![$((POSITIONAL_NAMES[$index], self.$index.to_decoded())),+]
281 }
282 }
283 };
284}
285
286impl_query_args_for_tuple!(0: A);
287impl_query_args_for_tuple!(0: A, 1: B);
288impl_query_args_for_tuple!(0: A, 1: B, 2: C);
289impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D);
290impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
291impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
292impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
293impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
294impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
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);
296impl_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);
297impl_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);
298
299#[macro_export]
312macro_rules! named_args {
313 ($($key:expr => $value:expr,)+) => { $crate::named_args!($($key => $value),+) };
314 ($($key:expr => $value:expr),*) => {{
315 let mut args = ::std::collections::HashMap::<&str, $crate::ValueOpt>::new();
316 $(
317 args.insert($key, $crate::ValueOpt::from($value));
318 )*
319 args
320 }};
321}