codama_korok_plugins/
plugin.rs1use codama_errors::CodamaResult;
2use codama_korok_visitors::KorokVisitable;
3
4pub trait KorokPlugin {
5 fn run(
6 &self,
7 visitable: &mut dyn KorokVisitable,
8 next: &dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()>,
9 ) -> CodamaResult<()>;
10}
11
12pub type ResolvePluginsResult<'a> = Box<dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()> + 'a>;
13
14pub fn resolve_plugins<'a>(plugins: &'a [Box<dyn KorokPlugin + 'a>]) -> ResolvePluginsResult<'a> {
46 plugins.iter().fold(
49 Box::new(|_: &mut dyn KorokVisitable| Ok(()))
51 as Box<dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()>>,
52 |next, plugin| {
54 Box::new(move |visitable: &mut dyn KorokVisitable| plugin.run(visitable, &next))
55 as Box<dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()>>
56 },
57 )
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use codama_korok_visitors::KorokVisitor;
64 use std::{cell::RefCell, rc::Rc};
65
66 struct LoggingPluging {
67 id: String,
68 logs: Rc<RefCell<Vec<String>>>,
69 }
70 impl LoggingPluging {
71 fn new(id: &str, logs: Rc<RefCell<Vec<String>>>) -> Self {
72 Self {
73 id: id.into(),
74 logs,
75 }
76 }
77 }
78 impl KorokPlugin for LoggingPluging {
79 fn run(
80 &self,
81 visitable: &mut dyn KorokVisitable,
82 next: &dyn Fn(&mut dyn KorokVisitable) -> CodamaResult<()>,
83 ) -> CodamaResult<()> {
84 self.logs
85 .borrow_mut()
86 .push(format!("Plugin {} - before", self.id));
87 next(visitable)?;
88 self.logs
89 .borrow_mut()
90 .push(format!("Plugin {} - after", self.id));
91 Ok(())
92 }
93 }
94
95 struct MockVisitable;
96 impl KorokVisitable for MockVisitable {
97 fn accept(&mut self, _visitor: &mut dyn KorokVisitor) -> CodamaResult<()> {
98 Ok(())
99 }
100 fn get_children(&mut self) -> Vec<&mut dyn KorokVisitable> {
101 Vec::new()
102 }
103 }
104
105 #[test]
106 fn test_resolve_plugins() -> CodamaResult<()> {
107 let logs = Rc::new(RefCell::new(Vec::new()));
108 let plugins: Vec<Box<dyn KorokPlugin>> = vec![
109 Box::new(LoggingPluging::new("A", logs.clone())),
110 Box::new(LoggingPluging::new("B", logs.clone())),
111 ];
112
113 let run_plugins = resolve_plugins(&plugins);
114 run_plugins(&mut MockVisitable)?;
115
116 assert_eq!(
117 logs.borrow().as_slice(),
118 &[
119 "Plugin B - before",
120 "Plugin A - before",
121 "Plugin A - after",
122 "Plugin B - after",
123 ]
124 );
125 Ok(())
126 }
127}