graphix-package-rand 0.8.0

A dataflow language for UIs and network programming, rand package
Documentation
#![doc(
    html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
    html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
)]
use anyhow::Result;
use graphix_compiler::{
    expr::ExprId, typ::FnType, Apply, BuiltIn, Event, ExecCtx, Node, Rt, Scope, UserEvent,
};
use graphix_package_core::CachedVals;
use netidx::subscriber::Value;
use netidx_value::ValArray;
use rand::{rng, seq::SliceRandom, RngExt};
use smallvec::{smallvec, SmallVec};

#[derive(Debug)]
struct Rand {
    args: CachedVals,
}

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Rand {
    const NAME: &str = "rand";
    const NEEDS_CALLSITE: bool = false;

    fn init<'a, 'b, 'c, 'd>(
        _ctx: &'a mut ExecCtx<R, E>,
        _typ: &'a FnType,
        _resolved: Option<&'d FnType>,
        _scope: &'b Scope,
        from: &'c [Node<R, E>],
        _top_id: ExprId,
    ) -> Result<Box<dyn Apply<R, E>>> {
        Ok(Box::new(Rand { args: CachedVals::new(from) }))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for Rand {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        macro_rules! gen_cases {
            ($start:expr, $end:expr, $($typ:ident),+) => {
                match ($start, $end) {
                    $(
                        (Value::$typ(start), Value::$typ(end)) if start < end => {
                            Some(Value::$typ(rng().random_range(*start..*end)))
                        }
                    ),+
                    _ => None
                }
            };
        }
        let up = self.args.update(ctx, from, event);
        if up {
            match &self.args.0[..] {
                [Some(start), Some(end), Some(_)] => gen_cases!(
                    start, end, F32, F64, I32, I64, Z32, Z64, U32, U64, V32, V64
                ),
                _ => None,
            }
        } else {
            None
        }
    }

    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
        self.args.clear()
    }
}

#[derive(Debug)]
struct Pick;

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Pick {
    const NAME: &str = "rand_pick";
    const NEEDS_CALLSITE: bool = false;

    fn init<'a, 'b, 'c, 'd>(
        _ctx: &'a mut ExecCtx<R, E>,
        _typ: &'a FnType,
        _resolved: Option<&'d FnType>,
        _scope: &'b Scope,
        _from: &'c [Node<R, E>],
        _top_id: ExprId,
    ) -> Result<Box<dyn Apply<R, E>>> {
        Ok(Box::new(Pick))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for Pick {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        from[0].update(ctx, event).and_then(|a| match a {
            Value::Array(a) if a.len() > 0 => {
                Some(a[rng().random_range(0..a.len())].clone())
            }
            _ => None,
        })
    }

    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
}

#[derive(Debug)]
struct Shuffle(SmallVec<[Value; 32]>);

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Shuffle {
    const NAME: &str = "rand_shuffle";
    const NEEDS_CALLSITE: bool = false;

    fn init<'a, 'b, 'c, 'd>(
        _ctx: &'a mut ExecCtx<R, E>,
        _typ: &'a FnType,
        _resolved: Option<&'d FnType>,
        _scope: &'b Scope,
        _from: &'c [Node<R, E>],
        _top_id: ExprId,
    ) -> Result<Box<dyn Apply<R, E>>> {
        Ok(Box::new(Shuffle(smallvec![])))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for Shuffle {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        from[0].update(ctx, event).and_then(|a| match a {
            Value::Array(a) => {
                self.0.extend(a.iter().cloned());
                self.0.shuffle(&mut rng());
                Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
            }
            _ => None,
        })
    }

    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
        self.0.clear()
    }
}

#[cfg(test)]
mod test;

graphix_derive::defpackage! {
    builtins => [
        Rand,
        Pick,
        Shuffle,
    ],
}