1use rustc_hash::FxHashMap;
44use std::collections::HashMap;
45use std::sync::Arc;
46
47use chrono::{DateTime, NaiveDate, Utc};
48use radixdb_core::SmartString;
49use radixdb_core::{Result, Value};
50
51pub use radixdb_core::ParamVec;
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub struct DecimalValue {
56 unscaled: i128,
57 precision: u8,
58 scale: u8,
59}
60
61impl DecimalValue {
62 pub fn try_new(unscaled: i128, precision: u8, scale: u8) -> Result<Self> {
64 Value::try_decimal(unscaled, precision, scale)?;
65 Ok(Self {
66 unscaled,
67 precision,
68 scale,
69 })
70 }
71
72 pub fn unscaled(self) -> i128 {
73 self.unscaled
74 }
75
76 pub fn precision(self) -> u8 {
77 self.precision
78 }
79
80 pub fn scale(self) -> u8 {
81 self.scale
82 }
83}
84
85pub trait ToParam {
99 fn to_param(&self) -> Value;
101}
102
103impl ToParam for i64 {
106 fn to_param(&self) -> Value {
107 Value::Integer(*self)
108 }
109}
110
111impl ToParam for i32 {
112 fn to_param(&self) -> Value {
113 Value::Integer(*self as i64)
114 }
115}
116
117impl ToParam for i16 {
118 fn to_param(&self) -> Value {
119 Value::Integer(*self as i64)
120 }
121}
122
123impl ToParam for i8 {
124 fn to_param(&self) -> Value {
125 Value::Integer(*self as i64)
126 }
127}
128
129impl ToParam for u32 {
130 fn to_param(&self) -> Value {
131 Value::Integer(*self as i64)
132 }
133}
134
135impl ToParam for u16 {
136 fn to_param(&self) -> Value {
137 Value::Integer(*self as i64)
138 }
139}
140
141impl ToParam for u8 {
142 fn to_param(&self) -> Value {
143 Value::Integer(*self as i64)
144 }
145}
146
147impl ToParam for f64 {
148 fn to_param(&self) -> Value {
149 Value::Float(*self)
150 }
151}
152
153impl ToParam for f32 {
154 fn to_param(&self) -> Value {
155 Value::Float(*self as f64)
156 }
157}
158
159impl ToParam for bool {
160 fn to_param(&self) -> Value {
161 Value::Boolean(*self)
162 }
163}
164
165impl ToParam for String {
166 fn to_param(&self) -> Value {
167 Value::Text(SmartString::new(self))
168 }
169}
170
171impl ToParam for &str {
172 fn to_param(&self) -> Value {
173 Value::Text(SmartString::from(*self))
174 }
175}
176
177impl ToParam for Arc<str> {
178 fn to_param(&self) -> Value {
179 Value::Text(SmartString::from(Arc::clone(self)))
180 }
181}
182
183impl ToParam for DateTime<Utc> {
184 fn to_param(&self) -> Value {
185 Value::Timestamp(*self)
186 }
187}
188
189impl ToParam for NaiveDate {
190 fn to_param(&self) -> Value {
191 let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).expect("valid Unix epoch date");
192 let days = self.signed_duration_since(epoch).num_days();
193 Value::date(i32::try_from(days).expect("chrono date range fits SQL DATE domain"))
194 }
195}
196
197impl ToParam for Vec<u8> {
198 fn to_param(&self) -> Value {
199 Value::bytes(self.clone())
200 }
201}
202
203impl ToParam for Vec<f32> {
204 fn to_param(&self) -> Value {
205 Value::vector(self.clone())
206 }
207}
208
209impl ToParam for uuid::Uuid {
210 fn to_param(&self) -> Value {
211 Value::uuid(*self.as_bytes())
212 }
213}
214
215impl ToParam for serde_json::Value {
216 fn to_param(&self) -> Value {
217 Value::try_json(self.to_string()).expect("serialized serde_json::Value is valid JSON")
218 }
219}
220
221impl ToParam for DecimalValue {
222 fn to_param(&self) -> Value {
223 Value::try_decimal(self.unscaled, self.precision, self.scale)
224 .expect("DecimalValue validates its invariant at construction")
225 }
226}
227
228impl ToParam for Value {
229 fn to_param(&self) -> Value {
230 self.clone()
231 }
232}
233
234impl<T: ToParam> ToParam for Option<T> {
235 fn to_param(&self) -> Value {
236 match self {
237 Some(v) => v.to_param(),
238 None => Value::null_unknown(),
239 }
240 }
241}
242
243impl<T: ToParam> ToParam for &T {
244 fn to_param(&self) -> Value {
245 (*self).to_param()
246 }
247}
248
249pub trait Params {
253 fn into_params(self) -> ParamVec;
256}
257
258impl Params for () {
260 fn into_params(self) -> ParamVec {
261 ParamVec::new()
262 }
263}
264
265impl Params for &[Value] {
267 fn into_params(self) -> ParamVec {
268 self.iter().cloned().collect()
269 }
270}
271
272impl Params for Vec<Value> {
274 fn into_params(self) -> ParamVec {
275 self.into_iter().collect()
276 }
277}
278
279impl Params for ParamVec {
281 fn into_params(self) -> ParamVec {
282 self
283 }
284}
285
286impl<const N: usize> Params for [Value; N] {
288 fn into_params(self) -> ParamVec {
289 self.into_iter().collect()
290 }
291}
292
293macro_rules! impl_params_for_tuple {
295 ($($idx:tt: $T:ident),+) => {
296 impl<$($T: ToParam),+> Params for ($($T,)+) {
297 fn into_params(self) -> ParamVec {
298 smallvec::smallvec![$(self.$idx.to_param()),+]
299 }
300 }
301 };
302}
303
304impl_params_for_tuple!(0: T0);
305impl_params_for_tuple!(0: T0, 1: T1);
306impl_params_for_tuple!(0: T0, 1: T1, 2: T2);
307impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3);
308impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4);
309impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5);
310impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6);
311impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7);
312impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8);
313impl_params_for_tuple!(0: T0, 1: T1, 2: T2, 3: T3, 4: T4, 5: T5, 6: T6, 7: T7, 8: T8, 9: T9);
314impl_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);
315impl_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);
316
317#[macro_export]
349macro_rules! params {
350 () => {
351 $crate::ParamVec::new()
352 };
353 ($($param:expr),+ $(,)?) => {
354 {
355 let mut params = $crate::ParamVec::new();
356 $(params.push($crate::ToParam::to_param(&$param));)+
357 params
358 }
359 };
360}
361
362#[derive(Debug, Clone, Default)]
388pub struct NamedParams {
389 params: FxHashMap<String, Value>,
390}
391
392impl NamedParams {
393 pub fn new() -> Self {
395 Self {
396 params: FxHashMap::default(),
397 }
398 }
399
400 pub fn with_capacity(capacity: usize) -> Self {
402 Self {
403 params: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
404 }
405 }
406
407 pub fn add<T: ToParam>(mut self, name: impl Into<String>, value: T) -> Self {
409 self.params.insert(name.into(), value.to_param());
410 self
411 }
412
413 pub fn insert<T: ToParam>(&mut self, name: impl Into<String>, value: T) {
415 self.params.insert(name.into(), value.to_param());
416 }
417
418 pub fn into_inner(self) -> FxHashMap<String, Value> {
420 self.params
421 }
422
423 pub fn as_map(&self) -> &FxHashMap<String, Value> {
425 &self.params
426 }
427}
428
429impl From<HashMap<String, Value>> for NamedParams {
430 fn from(params: HashMap<String, Value>) -> Self {
431 Self {
432 params: params.into_iter().collect(),
433 }
434 }
435}
436
437#[macro_export]
463macro_rules! named_params {
464 () => {
465 $crate::NamedParams::new()
466 };
467 ($($name:ident : $value:expr),+ $(,)?) => {
468 {
469 let mut params = $crate::NamedParams::new();
470 $(
471 params.insert(stringify!($name), $value);
472 )+
473 params
474 }
475 };
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 fn test_to_param_integers() {
484 assert_eq!(42i64.to_param(), Value::Integer(42));
485 assert_eq!(42i32.to_param(), Value::Integer(42));
486 assert_eq!(42i16.to_param(), Value::Integer(42));
487 assert_eq!(42i8.to_param(), Value::Integer(42));
488 assert_eq!(42u32.to_param(), Value::Integer(42));
489 assert_eq!(42u16.to_param(), Value::Integer(42));
490 assert_eq!(42u8.to_param(), Value::Integer(42));
491 }
492
493 #[test]
494 fn test_to_param_floats() {
495 assert_eq!(3.5f64.to_param(), Value::Float(3.5));
496 assert_eq!(3.5f32.to_param(), Value::Float(3.5f32 as f64));
497 }
498
499 #[test]
500 fn test_to_param_strings() {
501 assert_eq!("hello".to_param(), Value::text("hello"));
502 assert_eq!(String::from("world").to_param(), Value::text("world"));
503 }
504
505 #[test]
506 fn test_arc_str_boundary_preserves_allocation() {
507 let arc: Arc<str> = Arc::from("a heap string longer than the inline boundary");
508 let ptr = arc.as_ptr();
509
510 let smart = SmartString::from(Arc::clone(&arc));
511 assert_eq!(smart.as_str().as_ptr(), ptr);
512 assert_eq!(Arc::strong_count(&arc), 2);
513 drop(smart);
514
515 let value = Value::text_arc(Arc::clone(&arc));
516 let Value::Text(text) = &value else {
517 panic!("text_arc must produce Value::Text");
518 };
519 assert_eq!(text.as_str().as_ptr(), ptr);
520 assert_eq!(Arc::strong_count(&arc), 2);
521 drop(value);
522
523 let param = arc.to_param();
524 let Value::Text(text) = ¶m else {
525 panic!("Arc<str>::to_param must produce Value::Text");
526 };
527 assert_eq!(text.as_str().as_ptr(), ptr);
528 assert_eq!(Arc::strong_count(&arc), 2);
529 }
530
531 #[test]
532 fn test_to_param_bool() {
533 assert_eq!(true.to_param(), Value::Boolean(true));
534 assert_eq!(false.to_param(), Value::Boolean(false));
535 }
536
537 #[test]
538 fn test_to_param_option() {
539 assert_eq!(Some(42i64).to_param(), Value::Integer(42));
540 assert!(Option::<i64>::None.to_param().is_null());
541 }
542
543 #[test]
544 fn test_params_empty() {
545 let params: ParamVec = ().into_params();
546 assert!(params.is_empty());
547 }
548
549 #[test]
550 fn test_params_tuple() {
551 let params = (1i64, "hello", 3.5f64).into_params();
552 assert_eq!(params.len(), 3);
553 assert_eq!(params[0], Value::Integer(1));
554 assert_eq!(params[1], Value::text("hello"));
555 assert_eq!(params[2], Value::Float(3.5));
556 }
557
558 #[test]
559 fn test_params_macro() {
560 let p = params![1, "hello", 3.5];
561 let params = p.into_params();
562 assert_eq!(params.len(), 3);
563 assert_eq!(params[0], Value::Integer(1));
564 assert_eq!(params[1], Value::text("hello"));
565 assert_eq!(params[2], Value::Float(3.5));
566 }
567
568 #[test]
569 fn test_params_macro_empty() {
570 let p = params![];
571 let params: ParamVec = p.into_params();
572 assert!(params.is_empty());
573 }
574
575 #[test]
576 fn test_params_with_option() {
577 let name: Option<&str> = Some("Alice");
578 let age: Option<i32> = None;
579 let params = (1i64, name, age).into_params();
580
581 assert_eq!(params.len(), 3);
582 assert_eq!(params[0], Value::Integer(1));
583 assert_eq!(params[1], Value::text("Alice"));
584 assert!(params[2].is_null());
585 }
586
587 #[test]
588 fn test_params_from_param_vec() {
589 let mut pv = ParamVec::new();
591 pv.push(Value::Integer(1));
592 pv.push(Value::text("hello"));
593 pv.push(Value::Float(3.5));
594
595 let result = pv.into_params();
596 assert_eq!(result.len(), 3);
597 assert_eq!(result[0], Value::Integer(1));
598 assert_eq!(result[1], Value::text("hello"));
599 assert_eq!(result[2], Value::Float(3.5));
600 }
601
602 #[test]
603 fn test_params_from_empty_param_vec() {
604 let pv = ParamVec::new();
605 let result = pv.into_params();
606 assert!(result.is_empty());
607 }
608}