1use 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 { $($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 [] => {
77 $crate::params::Params::None
78 };
79
80 [ $($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}