directed/
lib.rs

1#![doc = include_str!("../README.md")]
2mod graphs;
3mod node;
4mod registry;
5mod stage;
6mod types;
7mod error;
8
9pub use directed_stage_macro::stage;
10pub use graphs::{EdgeInfo, Graph};
11pub use node::{AnyNode, Node};
12pub use registry::Registry;
13pub use stage::{EvalStrategy, ReevaluationRule, RefType, Stage};
14pub use types::{DataLabel, NodeOutput};
15pub use error::*;
16
17#[cfg(test)]
18mod tests {
19    extern crate self as directed;
20    use super::*;
21    use directed_stage_macro::stage;
22    use std::sync::atomic::{AtomicUsize, Ordering};
23
24    // A simple sanity-check test that doesn't try anything interesting
25    #[test]
26    fn basic_macro_test() {
27        #[stage(lazy, cache_last)]
28        fn TinyStage1() -> String {
29            println!("Running stage 1");
30            String::from("This is the output!")
31        }
32
33        #[stage(lazy, cache_last)]
34        fn TinyStage2(input: String, input2: String) -> String {
35            println!("Running stage 2");
36            input.to_uppercase() + " [" + &input.chars().count().to_string() + " chars]"
37        }
38
39        #[stage(cache_last)]
40        fn TinyStage3(input: String) {
41            println!("Running stage 3");
42            assert_eq!("THIS IS THE OUTPUT! [19 chars]", input);
43        }
44
45        let mut registry = Registry::new();
46        let node_1 = registry.register(TinyStage1::new());
47        let node_2 = registry.register(TinyStage2::new());
48        let node_3 = registry.register(TinyStage3::new());
49        let graph = graph! {
50            nodes: [node_1, node_2, node_3],
51            connections: {
52                node_1: _ => node_2: input,
53                node_1: _ => node_2: input2,
54                node_2: _ => node_3: input,
55            }
56        }
57        .unwrap();
58
59        graph.execute(&mut registry).unwrap();
60    }
61
62    // Test multiple output stages
63    #[test]
64    fn multiple_output_stage_test() {
65        #[stage(out(number: i32, text: String))]
66        fn MultiOutputStage() -> NodeOutput {
67            let value1 = 42;
68            let value2 = String::from("Hello");
69            output! {
70                number: value1,
71                text: value2
72            }
73        }
74
75        #[stage]
76        fn ConsumerStage1(number: i32) {
77            assert_eq!(number, 42);
78        }
79
80        #[stage]
81        fn ConsumerStage2(text: String) {
82            assert_eq!(text, "Hello");
83        }
84
85        let mut registry = Registry::new();
86        let producer = registry.register(MultiOutputStage::new());
87        let consumer1 = registry.register(ConsumerStage1::new());
88        let consumer2 = registry.register(ConsumerStage2::new());
89
90        let graph = graph! {
91            nodes: [producer, consumer1, consumer2],
92            connections: {
93                producer: number => consumer1: number,
94                producer: text => consumer2: text,
95            }
96        }
97        .unwrap();
98
99        graph.execute(&mut registry).unwrap();
100    }
101
102    // Test evaluating lazy vs urgent nodes
103    #[test]
104    fn lazy_and_urgent_eval_test() {
105        static COUNTER: AtomicUsize = AtomicUsize::new(0);
106
107        #[stage(lazy, cache_last)]
108        fn LazyStage() -> i32 {
109            COUNTER.fetch_add(1, Ordering::SeqCst);
110            42
111        }
112
113        #[stage(cache_last)]
114        fn UrgentStage(input: i32) {
115            assert_eq!(input, 42);
116            assert_eq!(COUNTER.load(Ordering::SeqCst), 1);
117        }
118
119        let mut registry = Registry::new();
120        let lazy_node = registry.register(LazyStage::new());
121        let urgent_node = registry.register(UrgentStage::new());
122
123        let graph = graph! {
124            nodes: [lazy_node, urgent_node],
125            connections: {
126                lazy_node: _ => urgent_node: input,
127            }
128        }
129        .unwrap();
130
131        // Reset counter
132        COUNTER.store(0, Ordering::SeqCst);
133
134        // Execute should evaluate LazyStage because UrgentStage depends on it
135        graph.execute(&mut registry).unwrap();
136    }
137
138    // Test transparent vs opaque reevaluation rules
139    #[test]
140    fn transparent_opaque_reevaluation_test() {
141        static TRANSPARENT_COUNTER: AtomicUsize = AtomicUsize::new(0);
142        static OPAQUE_COUNTER: AtomicUsize = AtomicUsize::new(0);
143
144        #[stage(lazy, cache_last)]
145        fn SourceStage() -> i32 {
146            println!("SourceStage");
147            42
148        }
149
150        #[stage(lazy, cache_last)]
151        fn TransparentStage(input: i32) -> i32 {
152            println!("TransparentStage");
153            TRANSPARENT_COUNTER.fetch_add(1, Ordering::SeqCst);
154            input * 2
155        }
156
157        #[stage(lazy)]
158        fn OpaqueStage(input: &i32) -> i32 {
159            println!("OpaqueStage");
160            OPAQUE_COUNTER.fetch_add(1, Ordering::SeqCst);
161            input * 3
162        }
163
164        #[stage]
165        fn SinkStage(t_input: &i32, o_input: &i32) {
166            println!("SinkStage");
167            assert_eq!(*t_input, 84);
168            assert_eq!(*o_input, 126);
169        }
170
171        let mut registry = Registry::new();
172        let source = registry.register(SourceStage::new());
173        let transparent = registry.register(TransparentStage::new());
174        let opaque = registry.register(OpaqueStage::new());
175        let sink = registry.register(SinkStage::new());
176
177        let graph = graph! {
178            nodes: [source, transparent, opaque, sink],
179            connections: {
180                source: _ => transparent: input,
181                source: _ => opaque: input,
182                transparent: _ => sink: t_input,
183                opaque: _ => sink: o_input,
184            }
185        }
186        .unwrap();
187
188        // Reset counters
189        TRANSPARENT_COUNTER.store(0, Ordering::SeqCst);
190        OPAQUE_COUNTER.store(0, Ordering::SeqCst);
191
192        // First execution
193        graph.execute(&mut registry).unwrap();
194        assert_eq!(TRANSPARENT_COUNTER.load(Ordering::SeqCst), 1);
195        assert_eq!(OPAQUE_COUNTER.load(Ordering::SeqCst), 1);
196
197        // Second execution - transparent stage shouldn't execute again since inputs haven't changed
198        graph.execute(&mut registry).unwrap();
199        assert_eq!(TRANSPARENT_COUNTER.load(Ordering::SeqCst), 1); // Still 1
200        assert_eq!(OPAQUE_COUNTER.load(Ordering::SeqCst), 2); // Increased to 2
201
202        println!("{}", graph.generate_trace(&registry, vec![sink], vec![(opaque, "_".into(), sink, "o_input".into())]).create_mermaid_graph());
203    }
204
205    // Test graph cycle detection
206    #[test]
207    fn cycle_detection_test() {
208        #[stage]
209        fn StageA(input: i32) -> i32 {
210            input + 1
211        }
212
213        #[stage]
214        fn StageB(input: i32) -> i32 {
215            input * 2
216        }
217
218        let mut registry = Registry::new();
219        let node_a = registry.register(StageA::new());
220        let node_b = registry.register(StageB::new());
221
222        // Attempt to create a cyclic graph
223        let result = graph! {
224            nodes: [node_a, node_b],
225            connections: {
226                node_a: _ => node_b: input,
227                node_b: _ => node_a: input,
228            }
229        };
230
231        // The graph creation should fail due to cycle detection
232        assert!(result.is_err());
233    }
234
235    // Test registry functionality
236    #[test]
237    fn registry_operations_test() {
238        #[stage]
239        fn SimpleStage() -> i32 {
240            42
241        }
242
243        let mut registry = Registry::new();
244
245        // Register a node
246        let node_id = registry.register(SimpleStage::new());
247
248        // Validate node type
249        registry.validate_node_type::<SimpleStage>(node_id).unwrap();
250
251        // Validate incorrect type
252        #[stage]
253        fn OtherStage() -> String {
254            "hello".to_string()
255        }
256        assert!(registry.validate_node_type::<OtherStage>(node_id).is_err());
257
258        // Get node
259        assert!(registry.get(node_id).is_some());
260
261        // Get mutable node
262        assert!(registry.get_mut(node_id).is_some());
263
264        // Unregister
265        let node = registry
266            .unregister::<SimpleStage>(node_id)
267            .unwrap()
268            .unwrap();
269        assert!(node.stage.eval_strategy() == EvalStrategy::Urgent);
270
271        // Node no longer exists
272        assert!(registry.get(node_id).is_none());
273    }
274
275    // Test error handling when node doesn't exist
276    #[test]
277    fn nonexistent_node_test() {
278        let mut registry = Registry::new();
279
280        // Node ID that doesn't exist
281        let invalid_id = 9999;
282
283        // Various operations should fail
284        assert!(registry.get(invalid_id).is_none());
285        assert!(registry.get_mut(invalid_id).is_none());
286        assert!(registry.unregister_and_drop(invalid_id).is_err());
287    }
288
289    // Test type mismatches in connections
290    #[test]
291    fn type_mismatch_test() {
292        #[stage]
293        fn StringStage() -> String {
294            "Hello".to_string()
295        }
296
297        #[stage]
298        fn IntegerConsumer(_input: i32) {
299            // This should never execute due to type mismatch
300            panic!("Should not execute");
301        }
302
303        let mut registry = Registry::new();
304        let producer = registry.register(StringStage::new());
305        let consumer = registry.register(IntegerConsumer::new());
306
307        // Create graph with type-incompatible connection
308        let graph = graph! {
309            nodes: [producer, consumer],
310            connections: {
311                producer: _ => consumer: input,
312            }
313        }
314        .unwrap();
315
316        // Execution should fail due to type mismatch when flowing data
317        let result = graph.execute(&mut registry);
318        assert!(result.is_err());
319    }
320
321    // Test missing inputs
322    #[test]
323    fn missing_input_test() {
324        #[stage]
325        fn ConsumerStage(_input1: i32, _input2: String) {
326            // This should never execute due to missing input
327            panic!("Should not execute");
328        }
329
330        #[stage]
331        fn ProducerStage() -> i32 {
332            42
333        }
334
335        let mut registry = Registry::new();
336        let producer = registry.register(ProducerStage::new());
337        let consumer = registry.register(ConsumerStage::new());
338
339        // Only connect one of the required inputs
340        let graph = graph! {
341            nodes: [producer, consumer],
342            connections: {
343                producer: _ => consumer: input1,
344            }
345        }
346        .unwrap();
347
348        // Execution should fail due to missing input
349        let result = graph.execute(&mut registry);
350        assert!(result.is_err());
351    }
352
353    // Test DataLabel functionality
354    #[test]
355    fn data_label_test() {
356        let label1 = DataLabel::new("test");
357        let label2 = DataLabel::new("test");
358        let label3 = DataLabel::new("different");
359
360        assert_eq!(label1, label2);
361        assert_ne!(label1, label3);
362
363        let const_label = DataLabel::new_const("const");
364        assert_eq!(const_label.inner(), "const");
365
366        let from_str: DataLabel = "string".into();
367        assert_eq!(from_str.inner(), "string");
368    }
369
370    // Test graph with diamond pattern
371    #[test]
372    fn diamond_graph_test() {
373        #[stage]
374        fn Source() -> i32 {
375            10
376        }
377
378        #[stage]
379        fn PathA(input: i32) -> i32 {
380            input * 2
381        }
382
383        #[stage]
384        fn PathB(input: i32) -> i32 {
385            input + 5
386        }
387
388        #[stage]
389        fn Sink(a: i32, b: i32) {
390            assert_eq!(a, 20); // 10 * 2
391            assert_eq!(b, 15); // 10 + 5
392        }
393
394        let mut registry = Registry::new();
395        let source = registry.register(Source::new());
396        let path_a = registry.register(PathA::new());
397        let path_b = registry.register(PathB::new());
398        let sink = registry.register(Sink::new());
399
400        let graph = graph! {
401            nodes: [source, path_a, path_b, sink],
402            connections: {
403                source: _ => path_a: input,
404                source: _ => path_b: input,
405                path_a: _ => sink: a,
406                path_b: _ => sink: b,
407            }
408        }
409        .unwrap();
410
411        graph.execute(&mut registry).unwrap();
412    }
413
414    // Test accessing outputs by wrong name
415    #[test]
416    fn invalid_output_name_test() {
417        #[stage]
418        fn MultiOutputStage() -> NodeOutput {
419            output! {
420                output1: 42,
421                output2: "Hello".to_string()
422            }
423        }
424
425        #[stage]
426        fn ConsumerStage(_input: i32) {
427            // Should never execute
428            panic!("Should not execute");
429        }
430
431        let mut registry = Registry::new();
432        let producer = registry.register(MultiOutputStage::new());
433        let consumer = registry.register(ConsumerStage::new());
434
435        // Connect with non-existent output name
436        let graph = graph! {
437            nodes: [producer, consumer],
438            connections: {
439                producer: nonexistent => consumer: input,
440            }
441        }
442        .unwrap();
443
444        // Should fail because the output name doesn't exist
445        let result = graph.execute(&mut registry);
446        assert!(result.is_err());
447    }
448
449    /// Test nodes with internal state
450    #[test]
451    fn node_with_state_test() {
452        #[stage(state((u8, u8)))]
453        fn StateStage() {
454            assert_eq!(state.1, state.0 * 5);
455            state.0 += 1;
456            state.1 += 5;
457            println!("State is {}", state.1);
458        }
459
460        let mut registry = Registry::new();
461        // Note: If the state has an implementation of "default", the simple
462        // register can still be called instead
463        let node = registry.register_with_state(StateStage::new(), (1, 5));
464        let graph = graph! {
465            nodes: [node],
466            connections: {}
467        }
468        .unwrap();
469
470        // TODO: Actually return results so this test can be real (right now it would pass if state never updated)
471        graph.execute(&mut registry).unwrap();
472        graph.execute(&mut registry).unwrap();
473        graph.execute(&mut registry).unwrap();
474        graph.execute(&mut registry).unwrap();
475    }
476
477    // Test the output! macro
478    #[test]
479    fn output_macro_test() {
480        #[stage(out(number: i32, text: String, vector: Vec<i32>))]
481        fn ProduceOutput1() -> NodeOutput {
482            println!("Running ProduceOutput1");
483            let number = 42;
484            let text = "hello".to_string();
485            let vector = vec![1, 2, 3];
486
487            output! {
488                number,
489                text,
490                vector
491            }
492        }
493
494        #[stage]
495        fn ConsumeOutputs(num: i32, txt: String, vec: Vec<i32>) {
496            assert_eq!(num, 42);
497            assert_eq!(txt, "hello");
498            assert_eq!(vec, vec![1, 2, 3]);
499        }
500
501        let mut registry = Registry::new();
502        let producer = registry.register(ProduceOutput1::new());
503        let consumer = registry.register(ConsumeOutputs::new());
504
505        let graph = graph! {
506            nodes: [producer, consumer],
507            connections: {
508                producer: number => consumer: num,
509                producer: text => consumer: txt,
510                producer: vector => consumer: vec,
511            }
512        }
513        .unwrap();
514
515        graph.execute(&mut registry).unwrap();
516    }
517
518    // Test registry node type validation
519    #[test]
520    fn registry_type_validation_test() {
521        #[stage]
522        fn StageA() -> i32 {
523            42
524        }
525
526        #[stage]
527        fn StageB() -> String {
528            "hello".to_string()
529        }
530
531        let mut registry = Registry::new();
532        let node_a = registry.register(StageA::new());
533
534        // Correct type validation should succeed
535        assert!(registry.validate_node_type::<StageA>(node_a).is_ok());
536
537        // Incorrect type validation should fail
538        assert!(registry.validate_node_type::<StageB>(node_a).is_err());
539
540        // Unregistering with incorrect type should fail
541        assert!(registry.unregister::<StageB>(node_a).is_err());
542
543        // Unregistering with correct type should succeed
544        assert!(registry.unregister::<StageA>(node_a).is_ok());
545    }
546}