flowstdlib 1.0.0

The standard library of functions and flows for 'flow' programs
Documentation
use flowcore::errors::{bail, Result};
use flowcore::flow_output;
use flowcore::RunAgain;
use flowmacro::flow_function;
use serde_json::{json, Value};

/// Generate numbers within a Range
#[flow_function]
fn inner_range_split(inputs: &[Value]) -> Result<(Option<Value>, RunAgain)> {
    let min_and_max = inputs
        .first()
        .ok_or("Could not get min_and_max")?
        .as_array()
        .ok_or("Could not get min and max array")?;

    if min_and_max.len() != 2 {
        bail!("Range (array/number) should have two values");
    }

    let min = min_and_max
        .first()
        .ok_or("Could not get min")?
        .as_i64()
        .ok_or("Could not get min")?;
    let max = min_and_max
        .get(1)
        .ok_or("Could not get max")?
        .as_i64()
        .ok_or("Could not get max")?;

    if min == max {
        // if the two are the same, we are done, output as "number"
        flow_output!("number" => json!(min))
    } else {
        // split the range_split into two and output for further subdivision
        let bottom: Vec<i64> = vec![min, ((max - min) / 2) + min];

        let above_middle = ((max - min) / 2) + min + 1;
        if above_middle == max {
            // if the two are the same, we are done, output as "number"
            flow_output!(
                "bottom" => json!(bottom),
                "number" => json!(max),
            )
        } else {
            let top: Vec<i64> = vec![above_middle, max];
            flow_output!(
                "bottom" => json!(bottom),
                "top" => json!(top),
            )
        }
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod test {
    use serde_json::{json, Value};

    use super::inner_range_split;

    #[test]
    fn test_first_split() {
        let range = vec![1, 10];
        let (output, again) = inner_range_split(&[json!(range)]).expect("_range_split() failed");

        let result = output.expect("Could not get value from output");
        assert!(again);

        assert_eq!(
            result
                .pointer("/bottom")
                .expect("Could not get the /bottom from the output"),
            &json!([1, 5])
        );
        assert_eq!(
            result
                .pointer("/top")
                .expect("Could not get the /top from the output"),
            &json!([6, 10])
        );
    }

    #[test]
    fn test_entire_range() {
        let min = 1;
        let max = 10;
        let test_range: Vec<i32> = vec![min, max];
        let test_range_json = json!(test_range);

        // these are outputs of "top" or "bottom" that require feeding into further iterations of Range
        let mut requires_further_splitting: Vec<Value> = vec![test_range_json];

        // We accumulate the values sent on "sequence" and then compare at the end
        let mut acquired_set: Vec<Value> = vec![];

        while let Some(next) = requires_further_splitting.pop() {
            println!("Splitting: {next:?}");
            let (output, again) = inner_range_split(&[next]).expect("_range_split() failed");
            assert!(again);
            let result = output.expect("Could not get value from output");

            if let Some(bottom) = result.pointer("/bottom") {
                requires_further_splitting.push(bottom.clone());
            }
            if let Some(top) = result.pointer("/top") {
                requires_further_splitting.push(top.clone());
            }
            if let Some(sequence) = result.pointer("/number") {
                acquired_set.push(sequence.clone());
            }
        }

        assert_eq!(acquired_set.len(), 10);
        assert!(acquired_set.contains(&json!(1)));
        assert!(acquired_set.contains(&json!(2)));
        assert!(acquired_set.contains(&json!(3)));
        assert!(acquired_set.contains(&json!(4)));
        assert!(acquired_set.contains(&json!(5)));
        assert!(acquired_set.contains(&json!(6)));
        assert!(acquired_set.contains(&json!(7)));
        assert!(acquired_set.contains(&json!(8)));
        assert!(acquired_set.contains(&json!(9)));
        assert!(acquired_set.contains(&json!(10)));
    }
}