lash_core/
plugin_stack.rs1use 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}