use std::collections::HashMap;
use pylon_value::DecodedValue;
pub trait QueryArg {
fn to_decoded(&self) -> DecodedValue;
}
impl<T: QueryArg + ?Sized> QueryArg for &T {
fn to_decoded(&self) -> DecodedValue {
(**self).to_decoded()
}
}
impl QueryArg for DecodedValue {
fn to_decoded(&self) -> DecodedValue {
self.clone()
}
}
impl<T: QueryArg> QueryArg for Option<T> {
fn to_decoded(&self) -> DecodedValue {
match self {
Some(value) => value.to_decoded(),
None => DecodedValue::Null,
}
}
}
macro_rules! query_arg_via_into {
($($target:ty),* $(,)?) => {
$(
impl QueryArg for $target {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::from(self.clone())
}
}
)*
};
}
query_arg_via_into!(bool, i16, i32, i64, f32, f64, String, uuid::Uuid);
impl QueryArg for str {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Str(self.to_string())
}
}
impl QueryArg for Vec<u8> {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Bytes(self.clone())
}
}
macro_rules! query_arg_array {
($($element:ty),* $(,)?) => {
$(
impl QueryArg for Vec<$element> {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Array(self.iter().map(QueryArg::to_decoded).collect())
}
}
impl QueryArg for [$element] {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Array(self.iter().map(QueryArg::to_decoded).collect())
}
}
)*
};
}
query_arg_array!(bool, i16, i32, i64, f32, f64, String, uuid::Uuid);
impl QueryArg for chrono::DateTime<chrono::Utc> {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Timestamptz(pg_micros(self.naive_utc()))
}
}
impl QueryArg for chrono::NaiveDateTime {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Timestamp(pg_micros(*self))
}
}
impl QueryArg for chrono::NaiveDate {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Date(self.signed_duration_since(pg_epoch_date()).num_days() as i32)
}
}
impl QueryArg for chrono::NaiveTime {
fn to_decoded(&self) -> DecodedValue {
let midnight = chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("00:00:00 is a valid time");
DecodedValue::Time(
self.signed_duration_since(midnight)
.num_microseconds()
.unwrap_or_default(),
)
}
}
impl QueryArg for std::time::Duration {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Interval {
months: 0,
days: 0,
microseconds: i64::try_from(self.as_micros()).unwrap_or(i64::MAX),
}
}
}
impl QueryArg for serde_json::Value {
fn to_decoded(&self) -> DecodedValue {
DecodedValue::Str(self.to_string())
}
}
fn pg_epoch_date() -> chrono::NaiveDate {
chrono::NaiveDate::from_ymd_opt(2000, 1, 1).expect("2000-01-01 is a valid date")
}
fn pg_micros(value: chrono::NaiveDateTime) -> i64 {
let epoch = pg_epoch_date().and_hms_opt(0, 0, 0).expect("00:00:00 is a valid time");
value
.signed_duration_since(epoch)
.num_microseconds()
.unwrap_or(i64::MAX)
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValueOpt(DecodedValue);
impl<T: QueryArg> From<T> for ValueOpt {
fn from(value: T) -> Self {
ValueOpt(value.to_decoded())
}
}
impl ValueOpt {
pub fn into_decoded(self) -> DecodedValue {
self.0
}
}
impl From<ValueOpt> for DecodedValue {
fn from(value: ValueOpt) -> Self {
value.0
}
}
pub trait QueryArgs {
fn to_params(&self) -> Vec<(&str, DecodedValue)>;
}
impl QueryArgs for () {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
Vec::new()
}
}
impl QueryArgs for [(&str, DecodedValue)] {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
self.iter().map(|(name, value)| (*name, value.clone())).collect()
}
}
impl<const N: usize> QueryArgs for [(&str, DecodedValue); N] {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
self.as_slice().to_params()
}
}
impl QueryArgs for Vec<(&str, DecodedValue)> {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
self.as_slice().to_params()
}
}
impl QueryArgs for HashMap<&str, ValueOpt> {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
self.iter().map(|(name, value)| (*name, value.0.clone())).collect()
}
}
impl QueryArgs for HashMap<String, ValueOpt> {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
self.iter()
.map(|(name, value)| (name.as_str(), value.0.clone()))
.collect()
}
}
const POSITIONAL_NAMES: [&str; 12] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"];
macro_rules! impl_query_args_for_tuple {
($($index:tt : $param:ident),+) => {
impl<$($param: QueryArg),+> QueryArgs for ($($param,)+) {
fn to_params(&self) -> Vec<(&str, DecodedValue)> {
vec![$((POSITIONAL_NAMES[$index], self.$index.to_decoded())),+]
}
}
};
}
impl_query_args_for_tuple!(0: A);
impl_query_args_for_tuple!(0: A, 1: B);
impl_query_args_for_tuple!(0: A, 1: B, 2: C);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
impl_query_args_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
impl_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);
impl_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);
#[macro_export]
macro_rules! named_args {
($($key:expr => $value:expr,)+) => { $crate::named_args!($($key => $value),+) };
($($key:expr => $value:expr),*) => {{
let mut args = ::std::collections::HashMap::<&str, $crate::ValueOpt>::new();
$(
args.insert($key, $crate::ValueOpt::from($value));
)*
args
}};
}