Skip to main content

lash_core/
plugin_stack.rs

1use std::sync::Arc;
2
3use crate::PluginFactory;
4
5#[derive(Clone, Default)]
6pub struct PluginStack {
7    factories: Vec<Arc<dyn PluginFactory>>,
8}
9
10impl PluginStack {
11    pub fn new() -> Self {
12        Self::default()
13    }
14
15    pub fn from_factories(factories: impl IntoIterator<Item = Arc<dyn PluginFactory>>) -> Self {
16        Self {
17            factories: factories.into_iter().collect(),
18        }
19    }
20
21    pub fn factories(&self) -> &[Arc<dyn PluginFactory>] {
22        &self.factories
23    }
24
25    pub fn into_factories(self) -> Vec<Arc<dyn PluginFactory>> {
26        self.factories
27    }
28
29    pub fn push(&mut self, plugin: Arc<dyn PluginFactory>) -> &mut Self {
30        self.factories.push(plugin);
31        self
32    }
33
34    pub fn extend(
35        &mut self,
36        plugins: impl IntoIterator<Item = Arc<dyn PluginFactory>>,
37    ) -> &mut Self {
38        self.factories.extend(plugins);
39        self
40    }
41
42    pub fn remove(&mut self, id: &str) -> &mut Self {
43        self.factories.retain(|plugin| plugin.id() != id);
44        self
45    }
46
47    pub fn replace(&mut self, plugin: Arc<dyn PluginFactory>) -> &mut Self {
48        let id = plugin.id();
49        if let Some(slot) = self
50            .factories
51            .iter_mut()
52            .find(|existing| existing.id() == id)
53        {
54            *slot = plugin;
55        } else {
56            self.factories.push(plugin);
57        }
58        self
59    }
60
61    pub fn retain(&mut self, keep: impl FnMut(&Arc<dyn PluginFactory>) -> bool) -> &mut Self {
62        self.factories.retain(keep);
63        self
64    }
65
66    pub fn configure(mut self, configure: impl FnOnce(&mut PluginStack)) -> Self {
67        configure(&mut self);
68        self
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::PluginSpec;
76    use crate::plugin::StaticPluginFactory;
77
78    fn factory(id: &'static str) -> Arc<dyn PluginFactory> {
79        Arc::new(StaticPluginFactory::new(id, PluginSpec::new()))
80    }
81
82    fn ids(stack: &PluginStack) -> Vec<&'static str> {
83        stack
84            .factories()
85            .iter()
86            .map(|factory| factory.id())
87            .collect()
88    }
89
90    #[test]
91    fn construction_preserves_factory_order_and_identity() {
92        let alpha = factory("alpha");
93        let beta = factory("beta");
94
95        let stack = PluginStack::from_factories([Arc::clone(&alpha), Arc::clone(&beta)]);
96
97        assert_eq!(ids(&stack), vec!["alpha", "beta"]);
98        assert!(Arc::ptr_eq(&stack.factories()[0], &alpha));
99        assert!(Arc::ptr_eq(&stack.factories()[1], &beta));
100
101        let factories = stack.into_factories();
102        assert_eq!(
103            factories
104                .iter()
105                .map(|factory| factory.id())
106                .collect::<Vec<_>>(),
107            vec!["alpha", "beta"]
108        );
109        assert!(Arc::ptr_eq(&factories[0], &alpha));
110        assert!(Arc::ptr_eq(&factories[1], &beta));
111    }
112
113    #[test]
114    fn mutators_apply_by_plugin_id_without_reordering_unrelated_plugins() {
115        let alpha = factory("alpha");
116        let beta_v1 = factory("beta");
117        let beta_v2 = factory("beta");
118        let gamma_v1 = factory("gamma");
119        let gamma_v2 = factory("gamma");
120        let delta = factory("delta");
121
122        let mut stack = PluginStack::new();
123        stack
124            .push(Arc::clone(&alpha))
125            .push(Arc::clone(&beta_v1))
126            .extend([Arc::clone(&gamma_v1), Arc::clone(&delta)]);
127
128        assert_eq!(ids(&stack), vec!["alpha", "beta", "gamma", "delta"]);
129
130        stack.remove("beta");
131        assert_eq!(ids(&stack), vec!["alpha", "gamma", "delta"]);
132        assert!(
133            stack
134                .factories()
135                .iter()
136                .all(|factory| factory.id() != "beta")
137        );
138
139        stack.remove("missing");
140        assert_eq!(ids(&stack), vec!["alpha", "gamma", "delta"]);
141
142        stack.replace(Arc::clone(&gamma_v2));
143        assert_eq!(ids(&stack), vec!["alpha", "gamma", "delta"]);
144        assert!(Arc::ptr_eq(&stack.factories()[1], &gamma_v2));
145        assert!(!Arc::ptr_eq(&stack.factories()[1], &gamma_v1));
146
147        stack.replace(Arc::clone(&beta_v2));
148        assert_eq!(ids(&stack), vec!["alpha", "gamma", "delta", "beta"]);
149        assert!(Arc::ptr_eq(&stack.factories()[3], &beta_v2));
150
151        stack.retain(|factory| factory.id() != "delta");
152        assert_eq!(ids(&stack), vec!["alpha", "gamma", "beta"]);
153        assert!(Arc::ptr_eq(&stack.factories()[0], &alpha));
154    }
155
156    #[test]
157    fn configure_applies_the_closure_to_the_owned_stack() {
158        let alpha_v1 = factory("alpha");
159        let alpha_v2 = factory("alpha");
160        let beta = factory("beta");
161        let gamma = factory("gamma");
162
163        let stack = PluginStack::new().configure(|stack| {
164            stack
165                .push(Arc::clone(&alpha_v1))
166                .extend([Arc::clone(&beta), Arc::clone(&gamma)])
167                .remove("beta")
168                .replace(Arc::clone(&alpha_v2));
169        });
170
171        assert_eq!(ids(&stack), vec!["alpha", "gamma"]);
172        assert!(Arc::ptr_eq(&stack.factories()[0], &alpha_v2));
173        assert!(Arc::ptr_eq(&stack.factories()[1], &gamma));
174    }
175}