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