1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
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());
    }
}