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}