Skip to main content

reifydb_type/
params.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2025 ReifyDB
3
4use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7
8use crate::value::Value;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
11pub enum Params {
12	#[default]
13	None,
14	Positional(Vec<Value>),
15	Named(HashMap<String, Value>),
16}
17
18impl Params {
19	pub fn get_positional(&self, index: usize) -> Option<&Value> {
20		match self {
21			Params::Positional(values) => values.get(index),
22			_ => None,
23		}
24	}
25
26	pub fn get_named(&self, name: &str) -> Option<&Value> {
27		match self {
28			Params::Named(map) => map.get(name),
29			_ => None,
30		}
31	}
32
33	pub fn empty() -> Params {
34		Params::None
35	}
36}
37
38impl From<()> for Params {
39	fn from(_: ()) -> Self {
40		Params::None
41	}
42}
43
44impl From<Vec<Value>> for Params {
45	fn from(values: Vec<Value>) -> Self {
46		Params::Positional(values)
47	}
48}
49
50impl From<HashMap<String, Value>> for Params {
51	fn from(map: HashMap<String, Value>) -> Self {
52		Params::Named(map)
53	}
54}
55
56impl<const N: usize> From<[Value; N]> for Params {
57	fn from(values: [Value; N]) -> Self {
58		Params::Positional(values.to_vec())
59	}
60}
61
62#[macro_export]
63macro_rules! params {
64    // Named parameters with mixed keys: params!{ name: value, "key": value }
65    { $($key:tt : $value:expr),+ $(,)? } => {
66        {
67            let mut map = ::std::collections::HashMap::new();
68            $(
69                map.insert($crate::params_key!($key), $crate::value::into::IntoValue::into_value($value));
70            )*
71            $crate::params::Params::Named(map)
72        }
73    };
74
75    // Empty positional parameters
76    [] => {
77        $crate::params::Params::None
78    };
79
80    // Positional parameters: params![value1, value2, ...]
81    [ $($value:expr),+ $(,)? ] => {
82        {
83            let values = vec![
84                $($crate::value::into::IntoValue::into_value($value)),*
85            ];
86            $crate::params::Params::Positional(values)
87        }
88    };
89}
90
91#[macro_export]
92#[doc(hidden)]
93macro_rules! params_key {
94	($key:ident) => {
95		stringify!($key).to_string()
96	};
97	($key:literal) => {
98		$key.to_string()
99	};
100}
101
102#[cfg(test)]
103pub mod tests {
104	use super::*;
105	use crate::{params::Params, value::into::IntoValue};
106
107	#[test]
108	fn test_params_macro_positional() {
109		let params = params![42, true, "hello"];
110		match params {
111			Params::Positional(values) => {
112				assert_eq!(values.len(), 3);
113				assert_eq!(values[0], Value::Int4(42));
114				assert_eq!(values[1], Value::Boolean(true));
115				assert_eq!(values[2], Value::Utf8("hello".to_string()));
116			}
117			_ => panic!("Expected positional params"),
118		}
119	}
120
121	#[test]
122	fn test_params_macro_named() {
123		let params = params! {
124		    name: true,
125		    other: 42,
126		    message: "test"
127		};
128		match params {
129			Params::Named(map) => {
130				assert_eq!(map.len(), 3);
131				assert_eq!(map.get("name"), Some(&Value::Boolean(true)));
132				assert_eq!(map.get("other"), Some(&Value::Int4(42)));
133				assert_eq!(map.get("message"), Some(&Value::Utf8("test".to_string())));
134			}
135			_ => panic!("Expected named params"),
136		}
137	}
138
139	#[test]
140	fn test_params_macro_named_with_strings() {
141		let params = params! {
142		    "string_key": 100,
143		    ident_key: 200,
144		    "another-key": "value"
145		};
146		match params {
147			Params::Named(map) => {
148				assert_eq!(map.len(), 3);
149				assert_eq!(map.get("string_key"), Some(&Value::Int4(100)));
150				assert_eq!(map.get("ident_key"), Some(&Value::Int4(200)));
151				assert_eq!(map.get("another-key"), Some(&Value::Utf8("value".to_string())));
152			}
153			_ => panic!("Expected named params"),
154		}
155	}
156
157	#[test]
158	fn test_params_macro_empty() {
159		let params = params!();
160		assert_eq!(params, Params::None);
161
162		let params2 = params! {};
163		assert_eq!(params2, Params::None);
164
165		let params3 = params![];
166		assert_eq!(params3, Params::None);
167	}
168
169	#[test]
170	fn test_params_macro_with_values() {
171		let v1 = Value::Int8(100);
172		let v2 = 200i64.into_value();
173
174		let params = params![v1, v2, 300];
175		match params {
176			Params::Positional(values) => {
177				assert_eq!(values.len(), 3);
178				assert_eq!(values[0], Value::Int8(100));
179				assert_eq!(values[1], Value::Int8(200));
180				assert_eq!(values[2], Value::Int4(300));
181			}
182			_ => panic!("Expected positional params"),
183		}
184	}
185
186	#[test]
187	fn test_params_macro_trailing_comma() {
188		let params1 = params![1, 2, 3,];
189		let params2 = params! { a: 1, b: 2};
190
191		match params1 {
192			Params::Positional(values) => {
193				assert_eq!(values.len(), 3)
194			}
195			_ => panic!("Expected positional params"),
196		}
197
198		match params2 {
199			Params::Named(map) => assert_eq!(map.len(), 2),
200			_ => panic!("Expected named params"),
201		}
202	}
203}