graphix-package-sys 0.9.0

A dataflow language for UIs and network programming, sys package
Documentation
use anyhow::{bail, Result};
use arcstr::literal;
use chrono::Utc;
use graphix_compiler::{
    err, expr::ExprId, typ::FnType, Apply, BindId, BuiltIn, Event, ExecCtx, Node, Rt,
    Scope, UserEvent,
};
use graphix_package_core::{arity2, CachedVals};
use netidx::{publisher::FromValue, subscriber::Value};
use std::{ops::SubAssign, time::Duration};

#[derive(Debug)]
pub(crate) struct AfterIdle {
    args: CachedVals,
    id: Option<BindId>,
    eid: ExprId,
}

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for AfterIdle {
    const NAME: &str = "sys_time_after_idle";
    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(AfterIdle { args: CachedVals::new(from), id: None, eid: top_id }))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for AfterIdle {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        let mut up = [false; 2];
        self.args.update_diff(&mut up, ctx, from, event);
        let ((timeout, val), (timeout_up, val_up)) = arity2!(self.args.0, &up);
        match ((timeout, val), (timeout_up, val_up)) {
            ((Some(secs), _), (true, _)) | ((Some(secs), _), (_, true)) => {
                match secs.clone().cast_to::<Duration>() {
                    Ok(dur) => {
                        let id = BindId::new();
                        self.id = Some(id);
                        ctx.rt.ref_var(id, self.eid);
                        ctx.rt.set_timer(id, dur);
                        return None;
                    }
                    Err(_) => {
                        self.id = None;
                        return None;
                    }
                }
            }
            ((None, _), (_, _))
            | ((_, None), (_, _))
            | ((Some(_), Some(_)), (false, _)) => (),
        };
        self.id.and_then(|id| {
            if event.variables.contains_key(&id) {
                self.id = None;
                ctx.rt.unref_var(id, self.eid);
                self.args.0.get(1).and_then(|v| v.clone())
            } else {
                None
            }
        })
    }

    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
        if let Some(id) = self.id.take() {
            ctx.rt.unref_var(id, self.eid)
        }
    }

    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
        if let Some(id) = self.id.take() {
            ctx.rt.unref_var(id, self.eid);
        }
        self.args.clear()
    }
}

#[derive(Debug, Clone, Copy)]
enum Repeat {
    Yes,
    No,
    N(u64),
}

impl FromValue for Repeat {
    fn from_value(v: Value) -> Result<Self> {
        match v {
            Value::Bool(true) => Ok(Repeat::Yes),
            Value::Bool(false) => Ok(Repeat::No),
            v => match v.cast_to::<u64>() {
                Ok(n) => Ok(Repeat::N(n)),
                Err(_) => bail!("could not cast to repeat"),
            },
        }
    }
}

impl SubAssign<u64> for Repeat {
    fn sub_assign(&mut self, rhs: u64) {
        match self {
            Repeat::Yes | Repeat::No => (),
            Repeat::N(n) => *n -= rhs,
        }
    }
}

impl Repeat {
    fn will_repeat(&self) -> bool {
        match self {
            Repeat::No => false,
            Repeat::Yes => true,
            Repeat::N(n) => *n > 0,
        }
    }
}

#[derive(Debug)]
pub(crate) struct Timer {
    args: CachedVals,
    timeout: Option<Duration>,
    repeat: Repeat,
    id: Option<BindId>,
    eid: ExprId,
}

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Timer {
    const NAME: &str = "sys_time_timer";
    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(Self {
            args: CachedVals::new(from),
            timeout: None,
            repeat: Repeat::No,
            id: None,
            eid: top_id,
        }))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for Timer {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        macro_rules! error {
            () => {{
                self.id = None;
                self.timeout = None;
                self.repeat = Repeat::No;
                return Some(err!(
                    literal!("TimerError"),
                    "timer(per, rep): expected duration, bool or number >= 0"
                ));
            }};
        }
        macro_rules! schedule {
            ($dur:expr) => {{
                let id = BindId::new();
                self.id = Some(id);
                ctx.rt.ref_var(id, self.eid);
                ctx.rt.set_timer(id, $dur);
            }};
        }
        let mut up = [false; 2];
        self.args.update_diff(&mut up, ctx, from, event);
        let ((timeout, repeat), (timeout_up, repeat_up)) = arity2!(self.args.0, &up);
        match ((timeout, repeat), (timeout_up, repeat_up)) {
            ((None, Some(r)), (true, true)) | ((_, Some(r)), (false, true)) => {
                match r.clone().cast_to::<Repeat>() {
                    Err(_) => error!(),
                    Ok(repeat) => {
                        self.repeat = repeat;
                        if let Some(dur) = self.timeout {
                            if self.id.is_none() && repeat.will_repeat() {
                                schedule!(dur)
                            }
                        }
                    }
                }
            }
            ((Some(s), None), (true, _)) => match s.clone().cast_to::<Duration>() {
                Err(_) => error!(),
                Ok(dur) => self.timeout = Some(dur),
            },
            ((Some(s), Some(r)), (true, _)) => {
                match (s.clone().cast_to::<Duration>(), r.clone().cast_to::<Repeat>()) {
                    (Err(_), _) | (_, Err(_)) => error!(),
                    (Ok(dur), Ok(repeat)) => {
                        self.timeout = Some(dur);
                        self.repeat = repeat;
                        schedule!(dur)
                    }
                }
            }
            ((_, _), (false, false))
            | ((None, None), (_, _))
            | ((None, _), (true, false))
            | ((_, None), (false, true)) => (),
        }
        self.id.and_then(|id| event.variables.get(&id).map(|now| (id, now))).map(
            |(id, now)| {
                ctx.rt.unref_var(id, self.eid);
                self.id = None;
                self.repeat -= 1;
                if let Some(dur) = self.timeout {
                    if self.repeat.will_repeat() {
                        schedule!(dur)
                    }
                }
                now.clone()
            },
        )
    }

    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
        if let Some(id) = self.id.take() {
            ctx.rt.unref_var(id, self.eid);
        }
    }

    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
        self.args.clear();
        self.timeout = None;
        self.repeat = Repeat::No;
        if let Some(id) = self.id.take() {
            ctx.rt.unref_var(id, self.eid);
        }
    }
}

#[derive(Debug)]
pub(crate) struct Now;

impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Now {
    const NAME: &str = "sys_time_now";
    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(Self))
    }
}

impl<R: Rt, E: UserEvent> Apply<R, E> for Now {
    fn update(
        &mut self,
        ctx: &mut ExecCtx<R, E>,
        from: &mut [Node<R, E>],
        event: &mut Event<E>,
    ) -> Option<Value> {
        if from[0].update(ctx, event).is_some() {
            Some(Value::from(Utc::now()))
        } else {
            None
        }
    }

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