codama_korok_plugins/
plugin.rs

1use codama_errors::CodamaResult;
2use codama_korok_visitors::KorokVisitable;
3
4pub trait KorokPlugin {
5    fn on_initialized(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
6        Ok(())
7    }
8
9    fn on_fields_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
10        Ok(())
11    }
12
13    fn on_program_items_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
14        Ok(())
15    }
16
17    fn on_root_node_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
18        Ok(())
19    }
20}
21
22pub type ResolvePluginsResult<'a> = Box<dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()> + 'a>;
23
24/// Combine all plugins into a single function that runs them in sequence.
25pub fn resolve_plugins<'a>(plugins: &'a [Box<dyn KorokPlugin + 'a>]) -> ResolvePluginsResult<'a> {
26    Box::new(move |visitable: &mut dyn KorokVisitable| {
27        plugins
28            .iter()
29            .try_for_each(|plugin| plugin.on_initialized(visitable))?;
30        plugins
31            .iter()
32            .try_for_each(|plugin| plugin.on_fields_set(visitable))?;
33        plugins
34            .iter()
35            .try_for_each(|plugin| plugin.on_program_items_set(visitable))?;
36        plugins
37            .iter()
38            .try_for_each(|plugin| plugin.on_root_node_set(visitable))?;
39        Ok(())
40    })
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use codama_korok_visitors::KorokVisitor;
47    use std::{cell::RefCell, rc::Rc};
48
49    struct LoggingPluging {
50        id: String,
51        logs: Rc<RefCell<Vec<String>>>,
52    }
53    impl LoggingPluging {
54        fn new(id: &str, logs: Rc<RefCell<Vec<String>>>) -> Self {
55            Self {
56                id: id.into(),
57                logs,
58            }
59        }
60    }
61    impl KorokPlugin for LoggingPluging {
62        fn on_initialized(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
63            self.logs
64                .borrow_mut()
65                .push(format!("Plugin {} - initialized", self.id));
66            Ok(())
67        }
68        fn on_fields_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
69            self.logs
70                .borrow_mut()
71                .push(format!("Plugin {} - on_fields_set", self.id));
72            Ok(())
73        }
74        fn on_program_items_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
75            self.logs
76                .borrow_mut()
77                .push(format!("Plugin {} - on_program_items_set", self.id));
78            Ok(())
79        }
80        fn on_root_node_set(&self, _visitable: &mut dyn KorokVisitable) -> CodamaResult<()> {
81            self.logs
82                .borrow_mut()
83                .push(format!("Plugin {} - on_root_node_set", self.id));
84            Ok(())
85        }
86    }
87
88    struct MockVisitable;
89    impl KorokVisitable for MockVisitable {
90        fn accept(&mut self, _visitor: &mut dyn KorokVisitor) -> CodamaResult<()> {
91            Ok(())
92        }
93        fn get_children(&mut self) -> Vec<&mut dyn KorokVisitable> {
94            Vec::new()
95        }
96    }
97
98    #[test]
99    fn test_resolve_plugins() -> CodamaResult<()> {
100        let logs = Rc::new(RefCell::new(Vec::new()));
101        let plugins: Vec<Box<dyn KorokPlugin>> = vec![
102            Box::new(LoggingPluging::new("A", logs.clone())),
103            Box::new(LoggingPluging::new("B", logs.clone())),
104        ];
105
106        let run_plugins = resolve_plugins(&plugins);
107        run_plugins(&mut MockVisitable)?;
108
109        assert_eq!(
110            logs.borrow().as_slice(),
111            &[
112                "Plugin A - initialized",
113                "Plugin B - initialized",
114                "Plugin A - on_fields_set",
115                "Plugin B - on_fields_set",
116                "Plugin A - on_program_items_set",
117                "Plugin B - on_program_items_set",
118                "Plugin A - on_root_node_set",
119                "Plugin B - on_root_node_set",
120            ]
121        );
122        Ok(())
123    }
124}