Skip to main content

graphix_package_time/
lib.rs

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