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