Skip to main content

graphix_package_time/
lib.rs

1use anyhow::{bail, Result};
2use arcstr::literal;
3use chrono::Utc;
4use graphix_compiler::{
5    err, expr::ExprId, Apply, BindId, BuiltIn, Event, ExecCtx, Node, Rt, Scope, UserEvent,
6};
7use graphix_package_core::{arity2, deftype, CachedVals};
8use netidx::{publisher::FromValue, subscriber::Value};
9use std::{ops::SubAssign, time::Duration};
10
11#[derive(Debug)]
12struct AfterIdle {
13    args: CachedVals,
14    id: Option<BindId>,
15    eid: ExprId,
16}
17
18impl<R: Rt, E: UserEvent> BuiltIn<R, E> for AfterIdle {
19    const NAME: &str = "time_after_idle";
20    deftype!("fn([duration, Number], 'a) -> 'a");
21
22    fn init<'a, 'b, 'c>(
23        _ctx: &'a mut ExecCtx<R, E>,
24        _typ: &'a graphix_compiler::typ::FnType,
25        _scope: &'b Scope,
26        from: &'c [Node<R, E>],
27        top_id: ExprId,
28    ) -> Result<Box<dyn Apply<R, E>>> {
29        Ok(Box::new(AfterIdle { args: CachedVals::new(from), id: None, eid: top_id }))
30    }
31}
32
33impl<R: Rt, E: UserEvent> Apply<R, E> for AfterIdle {
34    fn update(
35        &mut self,
36        ctx: &mut ExecCtx<R, E>,
37        from: &mut [Node<R, E>],
38        event: &mut Event<E>,
39    ) -> Option<Value> {
40        let mut up = [false; 2];
41        self.args.update_diff(&mut up, ctx, from, event);
42        let ((timeout, val), (timeout_up, val_up)) = arity2!(self.args.0, &up);
43        match ((timeout, val), (timeout_up, val_up)) {
44            ((Some(secs), _), (true, _)) | ((Some(secs), _), (_, true)) => {
45                match secs.clone().cast_to::<Duration>() {
46                    Ok(dur) => {
47                        let id = BindId::new();
48                        self.id = Some(id);
49                        ctx.rt.ref_var(id, self.eid);
50                        ctx.rt.set_timer(id, dur);
51                        return None;
52                    }
53                    Err(_) => {
54                        self.id = None;
55                        return None;
56                    }
57                }
58            }
59            ((None, _), (_, _))
60            | ((_, None), (_, _))
61            | ((Some(_), Some(_)), (false, _)) => (),
62        };
63        self.id.and_then(|id| {
64            if event.variables.contains_key(&id) {
65                self.id = None;
66                ctx.rt.unref_var(id, self.eid);
67                self.args.0.get(1).and_then(|v| v.clone())
68            } else {
69                None
70            }
71        })
72    }
73
74    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
75        if let Some(id) = self.id.take() {
76            ctx.rt.unref_var(id, self.eid)
77        }
78    }
79
80    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
81        if let Some(id) = self.id.take() {
82            ctx.rt.unref_var(id, self.eid);
83        }
84        self.args.clear()
85    }
86}
87
88#[derive(Debug, Clone, Copy)]
89enum Repeat {
90    Yes,
91    No,
92    N(u64),
93}
94
95impl FromValue for Repeat {
96    fn from_value(v: Value) -> Result<Self> {
97        match v {
98            Value::Bool(true) => Ok(Repeat::Yes),
99            Value::Bool(false) => Ok(Repeat::No),
100            v => match v.cast_to::<u64>() {
101                Ok(n) => Ok(Repeat::N(n)),
102                Err(_) => bail!("could not cast to repeat"),
103            },
104        }
105    }
106}
107
108impl SubAssign<u64> for Repeat {
109    fn sub_assign(&mut self, rhs: u64) {
110        match self {
111            Repeat::Yes | Repeat::No => (),
112            Repeat::N(n) => *n -= rhs,
113        }
114    }
115}
116
117impl Repeat {
118    fn will_repeat(&self) -> bool {
119        match self {
120            Repeat::No => false,
121            Repeat::Yes => true,
122            Repeat::N(n) => *n > 0,
123        }
124    }
125}
126
127#[derive(Debug)]
128struct Timer {
129    args: CachedVals,
130    timeout: Option<Duration>,
131    repeat: Repeat,
132    id: Option<BindId>,
133    eid: ExprId,
134}
135
136impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Timer {
137    const NAME: &str = "time_timer";
138    deftype!(
139        "fn([duration, Number], [bool, Number]) -> Result<datetime, `TimerError(string)>"
140    );
141
142    fn init<'a, 'b, 'c>(
143        _ctx: &'a mut ExecCtx<R, E>,
144        _typ: &'a graphix_compiler::typ::FnType,
145        _scope: &'b Scope,
146        from: &'c [Node<R, E>],
147        top_id: ExprId,
148    ) -> Result<Box<dyn Apply<R, E>>> {
149        Ok(Box::new(Self {
150            args: CachedVals::new(from),
151            timeout: None,
152            repeat: Repeat::No,
153            id: None,
154            eid: top_id,
155        }))
156    }
157}
158
159impl<R: Rt, E: UserEvent> Apply<R, E> for Timer {
160    fn update(
161        &mut self,
162        ctx: &mut ExecCtx<R, E>,
163        from: &mut [Node<R, E>],
164        event: &mut Event<E>,
165    ) -> Option<Value> {
166        macro_rules! error {
167            () => {{
168                self.id = None;
169                self.timeout = None;
170                self.repeat = Repeat::No;
171                return Some(err!(
172                    literal!("TimerError"),
173                    "timer(per, rep): expected duration, bool or number >= 0"
174                ));
175            }};
176        }
177        macro_rules! schedule {
178            ($dur:expr) => {{
179                let id = BindId::new();
180                self.id = Some(id);
181                ctx.rt.ref_var(id, self.eid);
182                ctx.rt.set_timer(id, $dur);
183            }};
184        }
185        let mut up = [false; 2];
186        self.args.update_diff(&mut up, ctx, from, event);
187        let ((timeout, repeat), (timeout_up, repeat_up)) = arity2!(self.args.0, &up);
188        match ((timeout, repeat), (timeout_up, repeat_up)) {
189            ((None, Some(r)), (true, true)) | ((_, Some(r)), (false, true)) => {
190                match r.clone().cast_to::<Repeat>() {
191                    Err(_) => error!(),
192                    Ok(repeat) => {
193                        self.repeat = repeat;
194                        if let Some(dur) = self.timeout {
195                            if self.id.is_none() && repeat.will_repeat() {
196                                schedule!(dur)
197                            }
198                        }
199                    }
200                }
201            }
202            ((Some(s), None), (true, _)) => match s.clone().cast_to::<Duration>() {
203                Err(_) => error!(),
204                Ok(dur) => self.timeout = Some(dur),
205            },
206            ((Some(s), Some(r)), (true, _)) => {
207                match (s.clone().cast_to::<Duration>(), r.clone().cast_to::<Repeat>()) {
208                    (Err(_), _) | (_, Err(_)) => error!(),
209                    (Ok(dur), Ok(repeat)) => {
210                        self.timeout = Some(dur);
211                        self.repeat = repeat;
212                        schedule!(dur)
213                    }
214                }
215            }
216            ((_, _), (false, false))
217            | ((None, None), (_, _))
218            | ((None, _), (true, false))
219            | ((_, None), (false, true)) => (),
220        }
221        self.id.and_then(|id| event.variables.get(&id).map(|now| (id, now))).map(
222            |(id, now)| {
223                ctx.rt.unref_var(id, self.eid);
224                self.id = None;
225                self.repeat -= 1;
226                if let Some(dur) = self.timeout {
227                    if self.repeat.will_repeat() {
228                        schedule!(dur)
229                    }
230                }
231                now.clone()
232            },
233        )
234    }
235
236    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
237        if let Some(id) = self.id.take() {
238            ctx.rt.unref_var(id, self.eid);
239        }
240    }
241
242    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
243        self.args.clear();
244        self.timeout = None;
245        self.repeat = Repeat::No;
246        if let Some(id) = self.id.take() {
247            ctx.rt.unref_var(id, self.eid);
248        }
249    }
250}
251
252#[derive(Debug)]
253struct Now;
254
255impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Now {
256    const NAME: &str = "time_now";
257    deftype!("fn(Any) -> datetime");
258
259    fn init<'a, 'b, 'c>(
260        _ctx: &'a mut ExecCtx<R, E>,
261        _typ: &'a graphix_compiler::typ::FnType,
262        _scope: &'b Scope,
263        _from: &'c [Node<R, E>],
264        _top_id: ExprId,
265    ) -> Result<Box<dyn Apply<R, E>>> {
266        Ok(Box::new(Self))
267    }
268}
269
270impl<R: Rt, E: UserEvent> Apply<R, E> for Now {
271    fn update(
272        &mut self,
273        ctx: &mut ExecCtx<R, E>,
274        from: &mut [Node<R, E>],
275        event: &mut Event<E>,
276    ) -> Option<Value> {
277        if from[0].update(ctx, event).is_some() {
278            Some(Value::from(Utc::now()))
279        } else {
280            None
281        }
282    }
283
284    fn delete(&mut self, _ctx: &mut ExecCtx<R, E>) {}
285    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
286}
287
288graphix_derive::defpackage! {
289    builtins => [
290        AfterIdle,
291        Timer,
292        Now,
293    ],
294}