use rustc_hash::FxHashMap;
use std::collections::HashMap;
use std::sync::Arc;
use chrono::{DateTime, Utc};
use smallvec::SmallVec;
use crate::common::SmartString;
use crate::core::Value;
pub type ParamVec = SmallVec<[Value; 8]>;
pub trait ToParam {
fn to_param(&self) -> Value;
}
impl ToParam for i64 {
fn to_param(&self) -> Value {
Value::Integer(*self)
}
}
impl ToParam for i32 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for i16 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for i8 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for u32 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for u16 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for u8 {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for usize {
fn to_param(&self) -> Value {
Value::Integer(*self as i64)
}
}
impl ToParam for f64 {
fn to_param(&self) -> Value {
Value::Float(*self)
}
}
impl ToParam for f32 {
fn to_param(&self) -> Value {
Value::Float(*self as f64)
}
}
impl ToParam for bool {
fn to_param(&self) -> Value {
Value::Boolean(*self)
}
}
impl ToParam for String {
fn to_param(&self) -> Value {
Value::Text(SmartString::new(self))
}
}
impl ToParam for &str {
fn to_param(&self) -> Value {
Value::Text(SmartString::from(*self))
}
}
impl ToParam for Arc<str> {
fn to_param(&self) -> Value {
Value::Text(SmartString::from(self.as_ref()))
}
}
impl ToParam for DateTime<Utc> {
fn to_param(&self) -> Value {
Value::Timestamp(*self)
}
}
impl ToParam for Value {
fn to_param(&self) -> Value {
self.clone()
}
}
impl<T: ToParam> ToParam for Option<T> {
fn to_param(&self) -> Value {
match self {
Some(v) => v.to_param(),
None => Value::null_unknown(),
}
}
}
impl<T: ToParam> ToParam for &T {
fn to_param(&self) -> Value {
(*self).to_param()
}
}
pub trait Params {
fn into_params(self) -> ParamVec;
}
impl Params for () {
fn into_params(self) -> ParamVec {
ParamVec::new()
}
}
impl Params for &[Value] {
fn into_params(self) -> ParamVec {
self.iter().cloned().collect()
}
}
impl Params for Vec<Value> {
fn into_params(self) -> ParamVec {
self.into_iter().collect()
}
}
impl Params for ParamVec {
fn into_params(self) -> ParamVec {
self
}
}
impl<const N: usize> Params for [Value; N] {
fn into_params(self) -> ParamVec {
self.into_iter().collect()
}
}
macro_rules! impl_params_for_tuple {
($($idx:tt: $T:ident),+) => {
impl<$($T: ToParam),+> Params for ($($T,)+) {
fn into_params(self) -> ParamVec {
smallvec::smallvec![$(self.$idx.to_param()),+]
}
}
};
}
impl_params_for_tuple!(0: T0);
impl_params_for_tuple!(0: T0, 1: T1);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10);
impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9, 10: T10, 11: T11);
#[macro_export]
macro_rules! params {
() => {
()
};
($($param:expr),+ $(,)?) => {
($($param,)+)
};
}
#[derive(Debug, Clone, Default)]
pub struct NamedParams {
params: FxHashMap<String, Value>,
}
impl NamedParams {
pub fn new() -> Self {
Self {
params: FxHashMap::default(),
}
}
pub fn add<T: ToParam>(mut self, name: impl Into<String>, value: T) -> Self {
self.params.insert(name.into(), value.to_param());
self
}
pub fn insert<T: ToParam>(&mut self, name: impl Into<String>, value: T) {
self.params.insert(name.into(), value.to_param());
}
pub fn into_inner(self) -> FxHashMap<String, Value> {
self.params
}
pub fn as_map(&self) -> &FxHashMap<String, Value> {
&self.params
}
}
impl From<HashMap<String, Value>> for NamedParams {
fn from(params: HashMap<String, Value>) -> Self {
Self {
params: params.into_iter().collect(),
}
}
}
#[macro_export]
macro_rules! named_params {
() => {
$crate::NamedParams::new()
};
($($name:ident : $value:expr),+ $(,)?) => {
{
let mut params = $crate::NamedParams::new();
$(
params.insert(stringify!($name), $value);
)+
params
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_param_integers() {
assert_eq!(42i64.to_param(), Value::Integer(42));
assert_eq!(42i32.to_param(), Value::Integer(42));
assert_eq!(42i16.to_param(), Value::Integer(42));
assert_eq!(42i8.to_param(), Value::Integer(42));
assert_eq!(42u32.to_param(), Value::Integer(42));
assert_eq!(42u16.to_param(), Value::Integer(42));
assert_eq!(42u8.to_param(), Value::Integer(42));
}
#[test]
fn test_to_param_floats() {
assert_eq!(3.5f64.to_param(), Value::Float(3.5));
assert_eq!(3.5f32.to_param(), Value::Float(3.5f32 as f64));
}
#[test]
fn test_to_param_strings() {
assert_eq!("hello".to_param(), Value::text("hello"));
assert_eq!(String::from("world").to_param(), Value::text("world"));
}
#[test]
fn test_to_param_bool() {
assert_eq!(true.to_param(), Value::Boolean(true));
assert_eq!(false.to_param(), Value::Boolean(false));
}
#[test]
fn test_to_param_option() {
assert_eq!(Some(42i64).to_param(), Value::Integer(42));
assert!(Option::<i64>::None.to_param().is_null());
}
#[test]
fn test_params_empty() {
let params: ParamVec = ().into_params();
assert!(params.is_empty());
}
#[test]
fn test_params_tuple() {
let params = (1i64, "hello", 3.5f64).into_params();
assert_eq!(params.len(), 3);
assert_eq!(params[0], Value::Integer(1));
assert_eq!(params[1], Value::text("hello"));
assert_eq!(params[2], Value::Float(3.5));
}
#[test]
fn test_params_macro() {
let p = params![1, "hello", 3.5];
let params = p.into_params();
assert_eq!(params.len(), 3);
assert_eq!(params[0], Value::Integer(1));
assert_eq!(params[1], Value::text("hello"));
assert_eq!(params[2], Value::Float(3.5));
}
#[test]
fn test_params_macro_empty() {
let p = params![];
let params: ParamVec = p.into_params();
assert!(params.is_empty());
}
#[test]
fn test_params_with_option() {
let name: Option<&str> = Some("Alice");
let age: Option<i32> = None;
let params = (1i64, name, age).into_params();
assert_eq!(params.len(), 3);
assert_eq!(params[0], Value::Integer(1));
assert_eq!(params[1], Value::text("Alice"));
assert!(params[2].is_null());
}
#[test]
fn test_params_from_param_vec() {
let mut pv = ParamVec::new();
pv.push(Value::Integer(1));
pv.push(Value::text("hello"));
pv.push(Value::Float(3.5));
let result = pv.into_params();
assert_eq!(result.len(), 3);
assert_eq!(result[0], Value::Integer(1));
assert_eq!(result[1], Value::text("hello"));
assert_eq!(result[2], Value::Float(3.5));
}
#[test]
fn test_params_from_empty_param_vec() {
let pv = ParamVec::new();
let result = pv.into_params();
assert!(result.is_empty());
}
}