dust/
lib.rs

1use leptos::logging::log;
2use std::collections::HashMap;
3use std::collections::HashSet;
4use std::hash::Hash;
5
6pub mod file_handler;
7pub mod serve;
8
9pub use dust_macro::{DustState, dust_define_callback, dust_lib, dust_main};
10
11// Re-exports
12pub use console_error_panic_hook;
13pub use leptos;
14pub use leptos_meta;
15pub use leptos_router;
16pub use once_cell;
17pub use serde;
18
19#[cfg(feature = "ssr")]
20pub use tokio;
21
22#[cfg(feature = "hydrate")]
23pub use wasm_bindgen;
24
25pub use web_sys;
26
27pub struct Input<T> {
28    pub value: T,
29}
30
31#[derive(Clone, Copy)]
32pub enum OutputState {
33    NoChange,
34    Updated,
35}
36
37pub struct Output<T> {
38    pub value: T,
39    pub state: OutputState,
40}
41
42impl<T: Clone> Output<T> {
43    pub fn new(value: T) -> Output<T> {
44        Output {
45            value: value,
46            state: OutputState::NoChange,
47        }
48    }
49
50    pub fn set(&mut self, value: T) {
51        self.value = value;
52        self.state = OutputState::Updated;
53    }
54}
55
56#[derive(Clone)]
57pub struct StateCallback<I, V, S> {
58    pub name: &'static str,
59    pub cb: fn(&mut S) -> Vec<V>,
60    pub inputs: Vec<I>,
61    pub outputs: Vec<I>,
62}
63
64impl<I, S, V> StateCallback<I, V, S> {
65    pub fn new(name: &'static str, cb: fn(&mut S) -> Vec<V>, inputs: Vec<I>, outputs: Vec<I>) -> Self {
66        Self {
67            name,
68            cb,
69            inputs,
70            outputs,
71        }
72    }
73}
74
75impl<I, S, V> std::hash::Hash for StateCallback<I, V, S> {
76    fn hash<H>(&self, state: &mut H)
77    where
78        H: std::hash::Hasher,
79    {
80        let pointer = self.cb as *const ();
81        pointer.hash(state);
82        state.finish();
83    }
84}
85
86impl<I, V, S> PartialEq for StateCallback<I, V, S> {
87    fn eq(&self, other: &StateCallback<I, V, S>) -> bool {
88        let self_pointer = self.cb as *const ();
89        let other_pointer = other.cb as *const ();
90        return self_pointer == other_pointer;
91    }
92}
93
94#[derive(Clone)]
95pub struct StateCallbackWithId<I, V, S> {
96    pub id: usize,
97    pub callback: StateCallback<I, V, S>,
98}
99
100pub trait StateTypes {
101    type Identifier;
102    type Value;
103    type CallbackInfo;
104    type Context;
105}
106
107impl<I, V, S> Eq for StateCallback<I, V, S> {}
108
109pub trait ValueToIdentifier<I> {
110    fn to_identifier(&self) -> I;
111}
112
113pub trait ApplyUpdates<V> {
114    fn apply_updates(&mut self, updates: &Vec<V>);
115}
116
117pub struct Executor<I, V, S> {
118    callbacks: Vec<StateCallbackWithId<I, V, S>>,
119    input_to_callbacks: HashMap<I, Vec<usize>>,
120    // Maps callback ids to the ids of callbacks that are immediately triggered by them.
121    callback_to_dependants: HashMap<usize, Vec<usize>>,
122    // Maps callback ids to their index in the topological sort.
123    callback_to_topological_rank: HashMap<usize, usize>,
124}
125
126impl<I, V, S> Executor<I, V, S>
127where
128    I: Hash + PartialEq + Eq + Clone + Copy,
129    V: Clone + std::fmt::Debug + ValueToIdentifier<I>,
130    S: Clone + Default + ApplyUpdates<V>,
131{
132    pub fn new() -> Executor<I, V, S> {
133        let app = Executor {
134            callbacks: Vec::new(),
135            input_to_callbacks: HashMap::new(),
136            callback_to_dependants: HashMap::new(),
137            callback_to_topological_rank: HashMap::new(),
138        };
139        return app;
140    }
141
142    pub fn register_callback(&mut self, callback: StateCallback<I, V, S>) {
143        let id = self.callbacks.len();
144
145        self.callbacks.push(StateCallbackWithId {
146            id: id,
147            callback: callback,
148        });
149    }
150
151    pub fn init_callbacks(&mut self) {
152        for cb in self.callbacks.iter() {
153            for input in cb.callback.inputs.iter() {
154                if !self.input_to_callbacks.contains_key(input) {
155                    self.input_to_callbacks.insert(input.clone(), Vec::new());
156                }
157
158                let v = self.input_to_callbacks.get_mut(input).unwrap();
159                v.push(cb.id);
160            }
161        }
162
163        let mut callback_names: HashMap<usize, &'static str> = HashMap::new();
164        for cb in self.callbacks.iter() {
165            callback_names.insert(cb.id, cb.callback.name);
166            self.callback_to_dependants.insert(cb.id, Vec::new());
167            let edges = self.callback_to_dependants.get_mut(&cb.id).unwrap();
168
169            for output in cb.callback.outputs.iter() {
170                if let Some(deps) = self.input_to_callbacks.get(output) {
171                    for dep in deps.iter() {
172                        edges.push(*dep);
173                    }
174                }
175            }
176        }
177
178        let mut temp_marks: HashSet<usize> = HashSet::new();
179        let mut perm_marks: HashSet<usize> = HashSet::new();
180        let mut cycle: Vec<usize> = Vec::new();
181        let mut topological_order: Vec<usize> = Vec::new();
182
183        fn visit(
184            id: usize,
185            callback_names: &HashMap<usize, &'static str>,
186            callback_to_dependants: &HashMap<usize, Vec<usize>>,
187            temp_marks: &mut HashSet<usize>,
188            perm_marks: &mut HashSet<usize>,
189            cycle: &mut Vec<usize>,
190            topological_order: &mut Vec<usize>,
191        ) -> bool {
192            if perm_marks.contains(&id) {
193                return false;
194            }
195            if temp_marks.contains(&id) {
196                cycle.push(id);
197                return true;
198            }
199
200            temp_marks.insert(id);
201            for dep in callback_to_dependants.get(&id).unwrap().iter() {
202                if visit(
203                    *dep,
204                    callback_names,
205                    callback_to_dependants,
206                    temp_marks,
207                    perm_marks,
208                    cycle,
209                    topological_order,
210                ) {
211                    if cycle.len() == 1 || cycle.first().unwrap() != cycle.last().unwrap() {
212                        // Stop adding nodes when there's a complete cycle.
213                        cycle.push(id);
214                    }
215                    return true;
216                }
217            }
218            temp_marks.remove(&id);
219            perm_marks.insert(id);
220            topological_order.push(id);
221            return false;
222        }
223
224        for cb in self.callbacks.iter() {
225            if !perm_marks.contains(&cb.id) {
226                if visit(
227                    cb.id,
228                    &callback_names,
229                    &self.callback_to_dependants,
230                    &mut temp_marks,
231                    &mut perm_marks,
232                    &mut cycle,
233                    &mut topological_order,
234                ) {
235                    let cycle_description = cycle
236                        .iter()
237                        .rev()
238                        .map(|id| self.callbacks[*id].callback.name)
239                        .collect::<Vec<&str>>()
240                        .join(" -> ");
241                    panic!("Found callback cycle: {}", cycle_description);
242                }
243            }
244        }
245
246        topological_order.reverse();
247        let topological_order_description = topological_order
248            .iter()
249            .map(|id| self.callbacks[*id].callback.name)
250            .collect::<Vec<&str>>()
251            .join(" -> ");
252        log!(
253            "Computed callback tological order {}",
254            topological_order_description
255        );
256
257        for (rank, id) in topological_order.iter().enumerate() {
258            self.callback_to_topological_rank.insert(*id, rank);
259        }
260    }
261
262    pub fn get_execution_plan(&self, updated_inputs: &Vec<I>) -> Vec<usize> {
263        let mut execution_plan: Vec<usize> = Vec::new();
264        let mut visited_cb_ids: HashSet<usize> = HashSet::new();
265
266        fn visit(
267            id: usize,
268            callback_to_dependants: &HashMap<usize, Vec<usize>>,
269            execution_plan: &mut Vec<usize>,
270            visited_cb_ids: &mut HashSet<usize>,
271        ) {
272            if visited_cb_ids.contains(&id) {
273                return;
274            }
275
276            execution_plan.push(id);
277            visited_cb_ids.insert(id);
278            for dep in callback_to_dependants.get(&id).unwrap().iter() {
279                visit(*dep, callback_to_dependants, execution_plan, visited_cb_ids);
280            }
281        }
282
283        for input in updated_inputs.iter() {
284            for id in self.input_to_callbacks.get(input).unwrap().iter() {
285                visit(
286                    *id,
287                    &self.callback_to_dependants,
288                    &mut execution_plan,
289                    &mut visited_cb_ids,
290                );
291            }
292        }
293
294        execution_plan.sort_by_key(|id| self.callback_to_topological_rank.get(id).unwrap());
295        return execution_plan;
296    }
297
298    pub fn get_required_state(
299        &self,
300        updated_inputs: &Vec<I>,
301        execution_plan: &Vec<usize>,
302    ) -> HashSet<I> {
303        let mut available_inputs: HashSet<I> = HashSet::from_iter(updated_inputs.iter().cloned());
304        let mut required_state: HashSet<I> = HashSet::new();
305
306        for id in execution_plan.iter() {
307            let callback = &self.callbacks[*id].callback;
308            for input in callback.inputs.iter() {
309                if !available_inputs.contains(input) {
310                    required_state.insert(*input);
311                }
312            }
313            for output in callback.outputs.iter() {
314                available_inputs.insert(*output);
315            }
316        }
317
318        return required_state;
319    }
320
321    pub fn get_required_initialization_inputs(&self) -> HashSet<I> {
322        let mut required_inputs: HashSet<I> = HashSet::new();
323        for cb in self.callbacks.iter() {
324            for input in cb.callback.inputs.iter() {
325                required_inputs.insert(input.clone());
326            }
327        }
328
329        for cb in self.callbacks.iter() {
330            for output in cb.callback.outputs.iter() {
331                // An input is not required if it's an output of another method
332                // (which means it will be computed during initialization).
333                if required_inputs.contains(output) {
334                    required_inputs.remove(output);
335                }
336            }
337        }
338        return required_inputs;
339    }
340
341    pub fn process_updates(&self, input_updates: Vec<V>, required_state: Vec<V>) -> Vec<V> {
342        let mut state = S::default();
343        state.apply_updates(&required_state);
344        state.apply_updates(&input_updates);
345
346        let updated_inputs = input_updates.iter().map(|v| v.to_identifier()).collect();
347        let execution_plan = self.get_execution_plan(&updated_inputs);
348
349        let mut output_updates: Vec<V> = Vec::new();
350        for id in execution_plan.iter() {
351            let callback = &self.callbacks[*id].callback;
352
353            let mut new_updates = (callback.cb)(&mut state);
354            state.apply_updates(&new_updates);
355            output_updates.append(&mut new_updates);
356        }
357
358        println!("output_updates: {:?}", output_updates);
359        return output_updates;
360    }
361}