use mssql_types::{SqlValue, ToSql, TypeError};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct NamedParam {
pub name: String,
pub value: SqlValue,
}
impl NamedParam {
pub fn new<S: Into<String>>(name: S, value: SqlValue) -> Self {
Self {
name: name.into(),
value,
}
}
pub fn from_value<S: Into<String>, T: ToSql>(name: S, value: &T) -> Result<Self, TypeError> {
Ok(Self {
name: name.into(),
value: value.to_sql()?,
})
}
}
pub trait ToParams {
fn to_params(&self) -> Result<Vec<NamedParam>, TypeError>;
fn param_count(&self) -> Option<usize> {
None
}
}
#[derive(Debug, Clone, Default)]
pub struct ParamList {
params: Vec<NamedParam>,
}
impl ParamList {
pub fn new() -> Self {
Self { params: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
params: Vec::with_capacity(capacity),
}
}
pub fn push(&mut self, param: NamedParam) {
self.params.push(param);
}
pub fn add<S: Into<String>, T: ToSql>(&mut self, name: S, value: &T) -> Result<(), TypeError> {
self.params.push(NamedParam::from_value(name, value)?);
Ok(())
}
pub fn as_slice(&self) -> &[NamedParam] {
&self.params
}
pub fn len(&self) -> usize {
self.params.len()
}
pub fn is_empty(&self) -> bool {
self.params.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &NamedParam> {
self.params.iter()
}
}
impl From<Vec<NamedParam>> for ParamList {
fn from(params: Vec<NamedParam>) -> Self {
Self { params }
}
}
impl IntoIterator for ParamList {
type Item = NamedParam;
type IntoIter = std::vec::IntoIter<NamedParam>;
fn into_iter(self) -> Self::IntoIter {
self.params.into_iter()
}
}
impl<'a> IntoIterator for &'a ParamList {
type Item = &'a NamedParam;
type IntoIter = std::slice::Iter<'a, NamedParam>;
fn into_iter(self) -> Self::IntoIter {
self.params.iter()
}
}
impl FromIterator<NamedParam> for ParamList {
fn from_iter<I: IntoIterator<Item = NamedParam>>(iter: I) -> Self {
Self {
params: iter.into_iter().collect(),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
struct TestParams {
name: String,
age: i32,
}
impl ToParams for TestParams {
fn to_params(&self) -> Result<Vec<NamedParam>, TypeError> {
Ok(vec![
NamedParam::from_value("name", &self.name)?,
NamedParam::from_value("age", &self.age)?,
])
}
fn param_count(&self) -> Option<usize> {
Some(2)
}
}
#[test]
fn test_to_params_manual_impl() {
let params = TestParams {
name: "Alice".to_string(),
age: 30,
};
let named_params = params.to_params().unwrap();
assert_eq!(named_params.len(), 2);
assert_eq!(named_params[0].name, "name");
assert_eq!(named_params[1].name, "age");
}
#[test]
fn test_named_param_creation() {
let param = NamedParam::from_value("test", &42i32).unwrap();
assert_eq!(param.name, "test");
assert!(matches!(param.value, SqlValue::Int(42)));
}
#[test]
fn test_param_list() {
let mut list = ParamList::new();
list.add("name", &"Alice").unwrap();
list.add("age", &30i32).unwrap();
assert_eq!(list.len(), 2);
assert!(!list.is_empty());
let names: Vec<&str> = list.iter().map(|p| p.name.as_str()).collect();
assert_eq!(names, vec!["name", "age"]);
}
#[test]
fn test_param_list_from_iterator() {
let params: ParamList = vec![
NamedParam::new("a", SqlValue::Int(1)),
NamedParam::new("b", SqlValue::Int(2)),
]
.into_iter()
.collect();
assert_eq!(params.len(), 2);
}
}