http-path-params 0.2.0

HTTP Path Params
Documentation
use alloc::{string::String, vec::Vec};
use core::fmt::{self, Debug, Display};

use downcast_rs::{impl_downcast, DowncastSync};
use dyn_clone::{clone_trait_object, DynClone};

mod impl_internal;
mod impl_path_param;

//
pub trait PathParams: DowncastSync + DynClone {
    fn size(&self) -> usize;
    fn value(&self, _index: usize) -> String;
    fn set_value(&mut self, _index: usize, _value: &str) -> Result<(), ValueSetError>;

    fn name(&self, _index: usize) -> Option<&str> {
        None
    }

    fn values(&self) -> Vec<String> {
        (0..self.size())
            .into_iter()
            .map(|i| self.value(i))
            .collect()
    }
    fn set_values(&mut self, values: &[String]) -> Result<(), ValueSetError> {
        if values.len() != self.size() {
            return Err(ValueSetError::SizeMismatch);
        }

        (0..self.size())
            .into_iter()
            .try_for_each(|i| self.set_value(i, values.get(i).expect("unreachable")))
    }
}

impl_downcast!(PathParams);
clone_trait_object!(PathParams);

impl Debug for (dyn PathParams) {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "PathParams{{values: {:?}}}", self.values())
    }
}
impl Debug for (dyn PathParams + Send + Sync) {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "PathParams{{values: {:?}}}", self.values())
    }
}

//
#[derive(Debug, Clone)]
pub enum ValueSetError {
    SizeMismatch,
    ParseFailed(String),
}
impl Display for ValueSetError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}
#[cfg(feature = "std")]
impl std::error::Error for ValueSetError {}

#[cfg(test)]
mod tests {
    use super::*;

    use alloc::{boxed::Box, string::ToString as _, vec};

    #[derive(Debug, Clone)]
    struct PathParamsStorage(Vec<Box<dyn PathParams>>);

    #[test]
    fn test_path_params_dyn_clone_and_debug() {
        let path_params_list = PathParamsStorage(vec![Box::new(1), Box::new("foo".to_string())]);
        #[allow(clippy::redundant_clone)]
        let path_params_list = path_params_list.clone();
        #[cfg(feature = "std")]
        println!("path_params_list: {:?}", path_params_list);
        assert_eq!(path_params_list.0.len(), 2);
    }

    #[test]
    fn test_path_params_downcast() {
        let path_params: Box<dyn PathParams> = Box::new("foo".to_string());
        assert_eq!(path_params.downcast_ref::<String>().unwrap(), "foo");
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_value_set_error_impl_std_error() {
        use crate::PathParam;

        fn doing() -> Result<(), Box<dyn std::error::Error>> {
            let mut path_params = (PathParam(1_usize), PathParam("foo".to_string()));
            path_params.set_value(0, "bar")?;
            Ok(())
        }
        assert!(doing().is_err());
    }
}