mssql_client/
to_params.rs1use mssql_types::{SqlValue, ToSql, TypeError};
39
40#[derive(Debug, Clone)]
42#[non_exhaustive]
43pub struct NamedParam {
44 pub name: String,
46 pub value: SqlValue,
48}
49
50impl NamedParam {
51 pub fn new<S: Into<String>>(name: S, value: SqlValue) -> Self {
53 Self {
54 name: name.into(),
55 value,
56 }
57 }
58
59 pub fn from_value<S: Into<String>, T: ToSql>(name: S, value: &T) -> Result<Self, TypeError> {
61 Ok(Self {
62 name: name.into(),
63 value: value.to_sql()?,
64 })
65 }
66}
67
68pub trait ToParams {
94 fn to_params(&self) -> Result<Vec<NamedParam>, TypeError>;
100
101 fn param_count(&self) -> Option<usize> {
105 None
106 }
107}
108
109#[derive(Debug, Clone, Default)]
114pub struct ParamList {
115 params: Vec<NamedParam>,
116}
117
118impl ParamList {
119 pub fn new() -> Self {
121 Self { params: Vec::new() }
122 }
123
124 pub fn with_capacity(capacity: usize) -> Self {
126 Self {
127 params: Vec::with_capacity(capacity),
128 }
129 }
130
131 pub fn push(&mut self, param: NamedParam) {
133 self.params.push(param);
134 }
135
136 pub fn add<S: Into<String>, T: ToSql>(&mut self, name: S, value: &T) -> Result<(), TypeError> {
138 self.params.push(NamedParam::from_value(name, value)?);
139 Ok(())
140 }
141
142 pub fn as_slice(&self) -> &[NamedParam] {
144 &self.params
145 }
146
147 pub fn len(&self) -> usize {
149 self.params.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
154 self.params.is_empty()
155 }
156
157 pub fn iter(&self) -> impl Iterator<Item = &NamedParam> {
159 self.params.iter()
160 }
161}
162
163impl From<Vec<NamedParam>> for ParamList {
164 fn from(params: Vec<NamedParam>) -> Self {
165 Self { params }
166 }
167}
168
169impl IntoIterator for ParamList {
170 type Item = NamedParam;
171 type IntoIter = std::vec::IntoIter<NamedParam>;
172
173 fn into_iter(self) -> Self::IntoIter {
174 self.params.into_iter()
175 }
176}
177
178impl<'a> IntoIterator for &'a ParamList {
179 type Item = &'a NamedParam;
180 type IntoIter = std::slice::Iter<'a, NamedParam>;
181
182 fn into_iter(self) -> Self::IntoIter {
183 self.params.iter()
184 }
185}
186
187impl FromIterator<NamedParam> for ParamList {
188 fn from_iter<I: IntoIterator<Item = NamedParam>>(iter: I) -> Self {
189 Self {
190 params: iter.into_iter().collect(),
191 }
192 }
193}
194
195#[cfg(test)]
196#[allow(clippy::unwrap_used)]
197mod tests {
198 use super::*;
199
200 struct TestParams {
201 name: String,
202 age: i32,
203 }
204
205 impl ToParams for TestParams {
206 fn to_params(&self) -> Result<Vec<NamedParam>, TypeError> {
207 Ok(vec![
208 NamedParam::from_value("name", &self.name)?,
209 NamedParam::from_value("age", &self.age)?,
210 ])
211 }
212
213 fn param_count(&self) -> Option<usize> {
214 Some(2)
215 }
216 }
217
218 #[test]
219 fn test_to_params_manual_impl() {
220 let params = TestParams {
221 name: "Alice".to_string(),
222 age: 30,
223 };
224
225 let named_params = params.to_params().unwrap();
226 assert_eq!(named_params.len(), 2);
227 assert_eq!(named_params[0].name, "name");
228 assert_eq!(named_params[1].name, "age");
229 }
230
231 #[test]
232 fn test_named_param_creation() {
233 let param = NamedParam::from_value("test", &42i32).unwrap();
234 assert_eq!(param.name, "test");
235 assert!(matches!(param.value, SqlValue::Int(42)));
236 }
237
238 #[test]
239 fn test_param_list() {
240 let mut list = ParamList::new();
241 list.add("name", &"Alice").unwrap();
242 list.add("age", &30i32).unwrap();
243
244 assert_eq!(list.len(), 2);
245 assert!(!list.is_empty());
246
247 let names: Vec<&str> = list.iter().map(|p| p.name.as_str()).collect();
248 assert_eq!(names, vec!["name", "age"]);
249 }
250
251 #[test]
252 fn test_param_list_from_iterator() {
253 let params: ParamList = vec![
254 NamedParam::new("a", SqlValue::Int(1)),
255 NamedParam::new("b", SqlValue::Int(2)),
256 ]
257 .into_iter()
258 .collect();
259
260 assert_eq!(params.len(), 2);
261 }
262}