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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use crate::params::ParamVal;
use crate::Parameters;

/// An error that occurs as a result of a failed conversion of a `ParameterVal`
#[derive(Debug)]
pub enum FromParameterValueError {
    /// The data that was being attempted for conversion was not of the expected type.
    UnexpectedType,
}

/// A trait that is used to create a type from a provided `ParamVal`. This
/// trait should be implemented when a type should be able to be converted from
/// a single `ParamVal` type.
///
/// # Example
/// ```
/// use attribution::FromParameterValue;
/// use attribution::ParamVal;
///
/// let param_val = ParamVal::Bool(true);
/// let from_result = bool::from_parameter_value(param_val);
/// assert_eq!(from_result.unwrap(), true);
/// ```
pub trait FromParameterValue: Sized {
    /// Tries to create a type from the provided `ParamVal`
    fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError>;
}

impl FromParameterValue for u64 {
    fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
        if let ParamVal::Int(val) = parameter_val {
            Ok(val)
        } else {
            Err(FromParameterValueError::UnexpectedType)
        }
    }
}

impl FromParameterValue for bool {
    fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
        if let ParamVal::Bool(val) = parameter_val {
            Ok(val)
        } else {
            Err(FromParameterValueError::UnexpectedType)
        }
    }
}

impl FromParameterValue for String {
    fn from_parameter_value(parameter_val: ParamVal) -> Result<Self, FromParameterValueError> {
        if let ParamVal::Str(val) = parameter_val {
            Ok(val)
        } else {
            Err(FromParameterValueError::UnexpectedType)
        }
    }
}

/// An error that occurs as a result of a failed conversion of a `Parameters`
/// struct
#[derive(Debug)]
pub enum FromParametersError {
    /// Indicates the error ocurred because a value for a specified parameter
    /// was not supplied.
    MissingParam { param_name: String },

    /// Indicates the error occurred because the value that was attempted for conversion
    /// was for the incorrect type.
    UnexpectedType,
}

/// A trait that is used to extract data from a `Parameters` struct.
pub trait FromParameters: Sized {
    /// Try to create a type from a parameter struct (`params`) for a paramter
    /// of a specific name (`param_name`).
    fn from_parameters(
        params: &mut Parameters,
        param_name: &str,
    ) -> Result<Self, FromParametersError>;
}

impl<T: FromParameterValue> FromParameters for T {
    fn from_parameters(
        params: &mut Parameters,
        param_name: &str,
    ) -> Result<Self, FromParametersError> {
        if let Some(parameter_val) = params.remove(param_name) {
            T::from_parameter_value(parameter_val).map_err(|err| match err {
                FromParameterValueError::UnexpectedType => FromParametersError::UnexpectedType,
            })
        } else {
            Err(FromParametersError::MissingParam {
                param_name: param_name.into(),
            })
        }
    }
}

impl<T: FromParameters> FromParameters for Option<T> {
    fn from_parameters(
        params: &mut Parameters,
        param_name: &str,
    ) -> Result<Self, FromParametersError> {
        Ok(T::from_parameters(params, param_name).ok())
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use crate::Parameters;

    #[test]
    fn from_parameters_bool() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Bool(true));
        let output = bool::from_parameters(&mut params, "foo");

        assert_eq!(output.unwrap(), true);
    }

    #[test]
    fn from_parameters_str() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Int(1));
        let output = u64::from_parameters(&mut params, "foo");

        assert_eq!(output.unwrap(), 1);
    }

    #[test]
    fn from_parameters_int() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Str("bar".into()));
        let output = String::from_parameters(&mut params, "foo");

        let right: String = "bar".into();
        assert_eq!(output.unwrap(), right);
    }

    #[test]
    fn from_parameters_bool_option() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Bool(true));
        let output = Option::<bool>::from_parameters(&mut params, "foo");

        assert_eq!(output.unwrap(), Some(true));
        let no_output = Option::<bool>::from_parameters(&mut params, "foo");
        assert_eq!(no_output.unwrap(), None);
    }

    #[test]
    fn from_parameters_str_option() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Int(1));
        let output = Option::<u64>::from_parameters(&mut params, "foo");

        assert_eq!(output.unwrap(), Some(1));
        let no_output = Option::<u64>::from_parameters(&mut params, "foo");
        assert_eq!(no_output.unwrap(), None);
    }

    #[test]
    fn from_parameters_int_option() {
        let mut params = Parameters::new();
        params.insert("foo".into(), ParamVal::Str("bar".into()));
        let output = Option::<String>::from_parameters(&mut params, "foo");

        let right: String = "bar".into();
        assert_eq!(output.unwrap(), Some(right));
        let no_output = Option::<String>::from_parameters(&mut params, "foo");
        assert_eq!(no_output.unwrap(), None);
    }
}