flowstdlib 1.0.0

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

const WIDTH: usize = 256;
const HEIGHT: usize = 128;

#[flow_function]
fn inner_histogram(bins: &Value) -> Result<(Option<Value>, RunAgain)> {
    let bins = bins.as_array().ok_or("Could not get bins as array")?;

    let max_count = bins
        .iter()
        .filter_map(Value::as_u64)
        .max()
        .unwrap_or(1)
        .max(1);

    let mut grid: Vec<Vec<u8>> = vec![vec![255; WIDTH]; HEIGHT];

    for (x, bin) in bins.iter().enumerate().take(WIDTH) {
        let count = bin.as_u64().unwrap_or(0);
        let bar_height = usize::try_from(count * HEIGHT as u64 / max_count)
            .unwrap_or(0)
            .min(HEIGHT);
        for y in 0..bar_height {
            if let Some(row) = grid.get_mut(HEIGHT - 1 - y) {
                if let Some(pixel) = row.get_mut(x) {
                    *pixel = 0;
                }
            }
        }
    }

    flow_output!("grid" => json!(grid))
}

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

    use super::inner_histogram;

    #[test]
    #[allow(clippy::indexing_slicing)]
    fn renders_histogram() {
        let mut bins = vec![json!(0); 256];
        bins[0] = json!(10);
        bins[128] = json!(5);
        bins[255] = json!(10);
        let bins_value = Value::Array(bins);
        let (result, _) = inner_histogram(&bins_value).expect("failed");
        let output = result.expect("no output");
        let grid = output.pointer("/grid").expect("no grid");
        let rows = grid.as_array().expect("not array");
        assert_eq!(rows.len(), 128);
        assert_eq!(
            rows.first()
                .expect("empty")
                .as_array()
                .expect("not array")
                .len(),
            256
        );
    }
}