http-path-params 0.2.1

HTTP Path Params
Documentation
use alloc::string::String;

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

use crate::PathParamsInfo;

mod impl_id;
mod impl_internal;
mod impl_path_param;
mod impl_username;

//
pub trait PathParams: DowncastSync + DynClone {
    fn size(&self) -> usize;

    fn get(&self, _index: usize) -> (Option<&str>, String);
    fn set(
        &mut self,
        _index: usize,
        _name_and_value: &(Option<&str>, &str),
    ) -> Result<(), SetError>;
    fn verify(&self, _index: usize, _name_and_value: &(Option<&str>, &str))
        -> Result<(), SetError>;

    fn info(&self) -> PathParamsInfo {
        PathParamsInfo(
            (0..self.size())
                .map(|i| {
                    let (name, value) = self.get(i);
                    (name.map(Into::into), value)
                })
                .collect(),
        )
    }
    fn set_from_info(&mut self, info: &PathParamsInfo) -> Result<(), SetError> {
        if info.0.len() != self.size() {
            return Err(SetError::InfoLengthMismatch);
        }
        (0..self.size()).try_for_each(|i| {
            let (name, value) = info.get(i).expect("unreachable");
            self.set(i, &(name.as_deref(), value.as_ref()))
        })
    }
}

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

impl core::fmt::Debug for dyn PathParams {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("PathParams").field(&self.info()).finish()
    }
}
impl core::fmt::Debug for dyn PathParams + Send {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("PathParams").field(&self.info()).finish()
    }
}
impl core::fmt::Debug for dyn PathParams + Send + Sync {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("PathParams").field(&self.info()).finish()
    }
}

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

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

    use alloc::{boxed::Box, string::ToString as _, vec, vec::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_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(0, &(None, "bar"))?;
            Ok(())
        }
        assert!(doing().is_err());
    }
}