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
use std::collections::HashMap;

use serde_json::value::{Value, to_value, from_value};

use errors::Result;


/// The global function type definition
pub type GlobalFn = Box<Fn(HashMap<String, Value>) -> Result<Value> + Sync + Send>;


pub fn make_range_fn() -> GlobalFn {
    Box::new(move |args| -> Result<Value> {
        let start = match args.get("start") {
            Some(val) => match from_value::<usize>(val.clone()) {
                Ok(v) => v,
                Err(_) => bail!("Global function `range` received start={} but `start` can only be a number"),
            },
            None => 0,
        };
        let step_by = match args.get("step_by") {
            Some(val) => match from_value::<usize>(val.clone()) {
                Ok(v) => v,
                Err(_) => bail!("Global function `range` received step_by={} but `step` can only be a number"),
            },
            None => 1,
        };
        let end = match args.get("end") {
            Some(val) => match from_value::<usize>(val.clone()) {
                Ok(v) => v,
                Err(_) => bail!("Global function `range` received end={} but `end` can only be a number"),
            },
            None => bail!("Global function `range` was called without a `end` argument"),
        };

        if start > end {
            bail!("Global function `range` was called without a `start` argument greater than the `end` one");
        }

        let mut i = start;
        let mut res = vec![];
        while i < end {
            res.push(i);
            i += step_by;
        }
        Ok(to_value(res).unwrap())
    })
}

//pub let range_fn = |args: HashMap<String, Value>| -> Result<Value> {
//    let start = match args.get("start") {
//        Some(val) => match from_value::<usize>(val.clone()) {
//            Ok(v) => v,
//            Err(_) => bail!("Global function `range` received start={} but `start` can only be a number"),
//        },
//        None => 0,
//    };
//    let step_by = match args.get("step_by") {
//        Some(val) => match from_value::<usize>(val.clone()) {
//            Ok(v) => v,
//            Err(_) => bail!("Global function `range` received step_by={} but `step` can only be a number"),
//        },
//        None => 1,
//    };
//    let end = match args.get("end") {
//        Some(val) => match from_value::<usize>(val.clone()) {
//            Ok(v) => v,
//            Err(_) => bail!("Global function `range` received end={} but `end` can only be a number"),
//        },
//        None => bail!("Global function `range` was called without a `end` argument"),
//    };
//
//    if start > end {
//        bail!("Global function `range` was called without a `start` argument greater than the `end` one");
//    }
//
//    let mut i = start;
//    let mut res = vec![];
//    while i < end {
//        res.push(i);
//        i += step_by;
//    }
//    Ok(to_value(res).unwrap())
//}


#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use serde_json::value::{to_value};

    use super::{make_range_fn};

    #[test]
    fn test_range_default() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());

        let res = make_range_fn()(args).unwrap();
        assert_eq!(res, to_value(vec![0,1,2,3,4]).unwrap());
    }

    #[test]
    fn test_range_start() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(1).unwrap());

        let res = make_range_fn()(args).unwrap();
        assert_eq!(res, to_value(vec![1,2,3,4]).unwrap());
    }

    #[test]
    fn test_range_start_greater_than_end() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(5).unwrap());
        args.insert("start".to_string(), to_value(6).unwrap());

        assert!(make_range_fn()(args).is_err());
    }

    #[test]
    fn test_range_step_by() {
        let mut args = HashMap::new();
        args.insert("end".to_string(), to_value(10).unwrap());
        args.insert("step_by".to_string(), to_value(2).unwrap());

        let res = make_range_fn()(args).unwrap();
        assert_eq!(res, to_value(vec![0,2,4,6,8]).unwrap());
    }
}