rs-jsonpath 0.1.0

JsonPath library
Documentation
use serde_json::Value;
use std::str::FromStr;
use tokenizer::s;

pub fn calc_from(array_len: usize,  args: &Vec<String>) -> Result<usize, String> {
    if args.len() < 1 { return Ok(0); }
    let mut from: i64 = i64::from_str(args[0].as_str()).unwrap_or(0);
    if from < 0 {
        from = array_len as i64 + from
    }
    match from {
        _ if from >= 0 => Ok(from as usize),
        _ => Err(format!("Error with range: negative bound"))
    }
}

pub fn calc_to(array_len: usize,  args: &Vec<String>) -> Result<usize, String> {
    if args.len() < 2 { return Ok(array_len); }
    let mut to: i64 = i64::from_str(args[1].as_str()).unwrap_or(array_len as i64);
    if to < 0 {
        to = 0
    }
    match to {
        _ if to >= 0 => Ok(to as usize),
        _ => Err(format!("Error with range: negative bound"))
    }
}

pub fn find_range(json: &Value, args: &Vec<String>) -> Result<Value,String> {
    json.as_array()
        .ok_or(s("Error with range"))
        .and_then(|array| {
            calc_from(array.len(), args)
                .and_then(|from| {
                    calc_to(array.len(), args)
                        .map(|to| array[from..to].to_vec())
                })
        })
        .map(|vec| Value::Array(vec))
}


pub fn search_key(json: &Value, key: &str, res: &Vec<Value>) -> Vec<Value> {
    match json.clone() {
       Value::Object(obj) => obj.iter().fold(vec![], |mut vect, (k, v)| {
                if k == key {
                    vect.push(v.clone());
                    vect.append(&mut search_key(v, key, res));
                } else {
                    vect.append(&mut search_key(v, key, res));
                }
                return vect;
            }),
       Value::Array(array) => array.iter().fold(vec![], |mut vect, value| {
                vect.append(&mut search_key(value, key, res));
                return vect;
            }),
       _ => res.clone()
    }
}

pub fn find_key(json: &Value, key: &str) -> Value {
    match json.clone() {
       Value::Object(obj) => obj.get(key).map(|elem| elem.clone()).unwrap_or(Value::Array(vec![])),
       Value::Array(array) => Value::Array(array.iter().map(|obj| {
                obj.get(key)
                    .map(|elem| elem.clone())
                    .unwrap_or(Value::Null)
            })
            .filter(|value| !value.is_null())
            .collect::<Vec<Value>>()),
       _ => Value::Array(vec![])
    }
}

pub fn find_indexes(json: &Value, args: &Vec<String>) -> Result<Value,String> {
    args.iter().map(|arg| {
        arg.parse()
        .map_err(|_| s("Err parsing argument"))
        .map(|idx| {
            json.as_array()
                .and_then(|array| {
                    let length = array.len();
                    let mut index: i64 = idx;
                    if index < 0 {
                        index = length as i64 + index
                    }
                    if index >= 0 {
                        array.get(index as usize)
                    } else {
                        None
                    }
                }).map(|elem| elem.clone())
                .unwrap_or(Value::Null)
        })
    }).collect::<Result<Vec<Value>, String>>()
    .map(|vec| Value::Array(vec))
}

#[cfg(test)]
mod tests {

    extern crate serde_json;
    use serde_json::Value;
    use tokenizer::s;
    use super::*;

    #[test]
    fn it_request_json_with_key() {
        let data: Value = serde_json::from_str(r#" { "authors": [{"name": "John Doe", "age": 43 }]} "#).unwrap();
        let res: Value = find_key(&data, "authors");
        let expected: Value = serde_json::from_str(r#"[{"name": "John Doe", "age": 43 }]"#).unwrap();
        assert_eq!(res, expected);
    }

    #[test]
    fn it_request_json_with_idx() {
        let data: Value  = serde_json::from_str(r#" [{"name": "John Doe", "age": 43 }, {"name": "Jane Doe", "age": 35 }] "#).unwrap();
        let res: Value = find_indexes(&data, &vec![s("1")]).unwrap();
        let expected: Value = serde_json::from_str(r#"[{"name": "Jane Doe", "age": 35 }]"#).unwrap();
        assert_eq!(res, expected);
    }

    #[test]
    fn it_request_json_with_range_wildcard() {
        let data: Value  = serde_json::from_str(r#" [{"name": "John Doe", "age": 43 }, {"name": "Jane Doe", "age": 35 }] "#).unwrap();
        let res: Value = find_range(&data, &vec![]).unwrap();
        let expected: Value = serde_json::from_str(r#"[{"name": "John Doe", "age": 43 }, {"name": "Jane Doe", "age": 35 }]"#).unwrap();
        assert_eq!(res, expected);
    }

    #[test]
    fn it_request_json_with_range_from_to() {
        let data: Value  = serde_json::from_str(r#" [{"name": "John Doe", "age": 43 }, {"name": "Jane Doe", "age": 35 }, {"name": "Jack Doe", "age": 3 }] "#).unwrap();
        let res: Value = find_range(&data, &vec![s("1"), s("3")]).unwrap();
        let expected: Value = serde_json::from_str(r#"[{"name": "Jane Doe", "age": 35 }, {"name": "Jack Doe", "age": 3 }]"#).unwrap();
        assert_eq!(res, expected);
    }

    #[test]
    fn it_search_key() {
        let data = r#" 
        {
            "firstName": "John",
            "lastName" : "doe",
            "age"      : 26,
            "address"  : {
                "streetAddress": "naist street",
                "city"         : "Nara",
                "type"   : "630-0192"
            },
            "phoneNumbers": [
                {
                "type"  : {"name": "iPhone", "type":"test"},
                "number": "0123-4567-8888"
                },
                {
                "type"  : "home",
                "number": "0123-4567-8910"
                }
            ]
        }
        "#;
        let data: Value = serde_json::from_str(data).unwrap();
        let expected = r#" 
        [
            "630-0192",
            {"name": "iPhone", "type":"test"},
            "test",
            "home"
        ]
        "#;
        let res = search_key(&data, "type", &vec![]);
        let expected: Value = serde_json::from_str(expected).unwrap();
        assert_eq!(Value::Array(res), expected);
    }
}