stepflow-json 0.0.3

StepFlow flow definitions using JSON
Documentation
use std::collections::HashMap;
use tinyjson::JsonValue;
use super::error::StepFlowParseError;

// FUTURE: would be cleaner as a trait on JsonValue

pub fn jsonval_get_obj<'a>(val: &'a JsonValue, key: &str) -> Result<&'a HashMap<String, JsonValue>, StepFlowParseError> {
    if let JsonValue::Object(o) = val {
        Ok(o)
    } else {
        Err(StepFlowParseError::WrongType { key: key.to_owned(), expected: "Object".to_owned() })
    }
}

pub fn jsonval_get_str<'a>(val: &'a JsonValue, key: &str) -> Result<&'a String, StepFlowParseError> {
    if let JsonValue::String(s) = val {
        Ok(s)
    } else {
        Err(StepFlowParseError::WrongType { key: key.to_owned(), expected: "String".to_owned() })
    }
}

pub fn jsonval_get_u64(val: &JsonValue, key: &str) -> Result<u64, StepFlowParseError> {
    if let JsonValue::Number(num) = val {
        let uint = *num as u64;
        if uint as f64 == *num {
            Ok(uint)
        } else {
            Err(StepFlowParseError::WrongType { key: key.to_owned(), expected: "uint".to_owned() })
        }
    } else {
        Err(StepFlowParseError::WrongType { key: key.to_owned(), expected: "Number".to_owned() })
    }
}

pub fn jsonval_obj_get_u64(jsonobj: &HashMap<String, JsonValue>, key: &str) -> Result<u64, StepFlowParseError> {
    if let Some(val) = jsonobj.get(key) {
        jsonval_get_u64(val, key)
    } else {
        Err(StepFlowParseError::MissingValue(key.to_owned()))
    }
}

pub fn jsonval_obj_get_obj<'a>(jsonobj: &'a HashMap<String, JsonValue>, key: &str) -> Result<&'a HashMap<String, JsonValue>, StepFlowParseError> {
    if let Some(val) = jsonobj.get(key) {
        jsonval_get_obj(val, key)
    } else {
        Err(StepFlowParseError::MissingValue(key.to_owned()))
    }
}

pub fn jsonval_obj_get_str<'a>(jsonobj: &'a HashMap<String, JsonValue>, key: &str) -> Result<&'a String, StepFlowParseError> {
    if let Some(val) = jsonobj.get(key) {
        jsonval_get_str(val, key)
    } else {
        Err(StepFlowParseError::MissingValue(key.to_owned()))
    }
}

pub fn jsonval_obj_get_vec<'a>(jsonobj: &'a HashMap<String, JsonValue>, key: &str) -> Result<&'a Vec<JsonValue>, StepFlowParseError> {
    if let Some(val) = jsonobj.get(key) {
        if let JsonValue::Array(arr) = val {
            return Ok(arr);
        } else {
            return Err(StepFlowParseError::WrongType { key: key.to_owned(), expected: "Array".to_owned() });
        }
    } else {
        return Err(StepFlowParseError::MissingValue(key.to_owned()));
    }
}


pub fn jsonval_obj_get_vec_str<'a>(jsonobj: &'a HashMap<String, JsonValue>, key: &str)
        -> Result<Vec<&'a String>, StepFlowParseError>
{
    jsonval_obj_get_vec(jsonobj, key)
        .and_then(|v| v.iter().map(|val| jsonval_get_str(val, key)).collect())
}

#[cfg(test)]
mod tests {
    use tinyjson::JsonValue;
    use super::jsonval_get_u64;

    #[test]
    fn u64_from_val() {
        let tests = vec![
            (JsonValue::Number(5.0), Some(5)),
            (JsonValue::Number(5.5), None),
            (JsonValue::Number(0.0), Some(0)),
            (JsonValue::Number(-1.0), None),
            (JsonValue::Number(u64::MAX as f64), Some(u64::MAX)),
            (JsonValue::Number(f64::MAX), None),
            (JsonValue::String("hi".to_owned()), None),
        ];
        for test in tests {
            let result = jsonval_get_u64(&test.0, "t");
            if test.1 == None {
                assert!(matches!(result, Err(_)));
            } else {
                assert_eq!(result.unwrap(), test.1.unwrap());
            }
        }
    }
}