1use crate::project::error::ProjectResult;
4
5use crate::utilities::Action;
6
7use parking_lot::RwLock;
8use std::any::type_name;
9use std::collections::{HashMap, HashSet, VecDeque};
10use std::fmt::{Debug, Formatter};
11
12use std::sync::Arc;
13
14pub mod extensions;
15
16pub trait Plugin<T: ?Sized>: Default {
18 fn apply_to(&self, target: &mut T) -> ProjectResult;
20
21 fn plugin_id(&self) -> &str {
23 type_name::<Self>()
24 }
25}
26
27pub trait PluginAware: Sized {
29 fn apply_plugin<P: Plugin<Self>>(&mut self) -> ProjectResult {
31 let manager = &mut self.plugin_manager().clone();
32 manager.apply::<P>(self)
33 }
34
35 fn plugin_manager(&self) -> &PluginManager<Self>;
37 fn plugin_manager_mut(&mut self) -> &mut PluginManager<Self>;
39}
40
41pub struct PluginApplied;
43
44type PluginManagerAction<T> = Box<dyn for<'a> FnOnce(&'a mut T) -> ProjectResult + Send + Sync>;
45
46pub struct PluginManager<T: PluginAware>(Arc<PluginManagerInner<T>>);
49
50impl<T: PluginAware> PluginManager<T> {
51 #[inline]
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn has_plugin(&self, id: &str) -> bool {
59 self.0.has_plugin(id)
60 }
61
62 pub fn has_plugin_ty<P: Plugin<T>>(&self) -> bool {
63 self.0.has_plugin_ty::<P>()
64 }
65
66 pub fn apply<P: Plugin<T>>(&mut self, target: &mut T) -> ProjectResult {
68 self.0.apply::<P>(target)
69 }
70
71 pub fn with_plugin<F: 'static>(&mut self, id: &str, target: &mut T, action: F) -> ProjectResult
73 where
74 T: 'static,
75 for<'a> F: FnOnce(&'a mut T) -> ProjectResult + Send + Sync,
76 {
77 self.0.with_plugin(id, target, action)
78 }
79}
80
81impl<T: PluginAware> Clone for PluginManager<T> {
82 fn clone(&self) -> Self {
83 Self(self.0.clone())
84 }
85}
86
87impl<T: PluginAware> Default for PluginManager<T> {
88 fn default() -> Self {
89 Self(Default::default())
90 }
91}
92
93impl<T: PluginAware> Debug for PluginManager<T> {
94 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("PluginManager").finish_non_exhaustive()
96 }
97}
98
99struct PluginManagerInner<T: PluginAware> {
100 applied: RwLock<HashSet<String>>,
101 lazy_with_plugins: RwLock<HashMap<String, VecDeque<PluginManagerAction<T>>>>,
102}
103
104impl<T: PluginAware> Default for PluginManagerInner<T> {
105 fn default() -> Self {
106 Self {
107 applied: Default::default(),
108 lazy_with_plugins: Default::default(),
109 }
110 }
111}
112
113impl<T: PluginAware> PluginManagerInner<T> {
114 pub fn has_plugin(&self, id: &str) -> bool {
116 self.applied.read().contains(id)
117 }
118
119 pub fn has_plugin_ty<P: Plugin<T>>(&self) -> bool {
120 let plugins = P::default();
121 let id = plugins.plugin_id();
122 self.has_plugin(id)
123 }
124
125 pub fn apply<P: Plugin<T>>(&self, target: &mut T) -> ProjectResult {
127 let type_name: &str = std::any::type_name::<P>();
128
129 trace!("attempting to apply plugin of type {type_name}");
130
131 let ret = if self.has_plugin_ty::<P>() {
132 trace!("plugin of type {type_name} already applied");
133 Ok(())
134 } else {
135 let plugin = P::default();
136 let id = plugin.plugin_id().to_string();
137 trace!("applying generated plugin of type {type_name} with id {id}");
138 plugin.apply_to(target)?;
139 trace!("added applied plugin id {id}");
140 self.applied.write().insert(id);
141
142 Ok(())
143 };
144 for applied in self.applied.read().clone() {
145 let mut lazy = self.lazy_with_plugins.write();
146 if let Some(actions) = lazy.get_mut(&*applied) {
147 let actions: Vec<_> = actions.drain(..).collect();
148 trace!(
149 "found {} delayed actions for plugin {} that will now be applied",
150 actions.len(),
151 applied
152 );
153 for action in actions {
154 action.execute(target)?;
155 }
156 }
157 }
158 ret
159 }
160
161 pub fn with_plugin<F: 'static>(&self, id: &str, target: &mut T, action: F) -> ProjectResult
163 where
164 T: 'static,
165 for<'a> F: FnOnce(&'a mut T) -> ProjectResult + Send + Sync,
166 {
167 if self.has_plugin(id) {
168 action.execute(target)
169 } else {
170 let id = id.to_string();
171 self.lazy_with_plugins
172 .write()
173 .entry(id)
174 .or_default()
175 .push_back(Box::new(action)
176 as Box<
177 dyn for<'b> FnOnce(&'b mut T) -> ProjectResult + Send + Sync,
178 >);
179 Ok(())
180 }
181 }
182}
183
184#[derive(Debug, thiserror::Error)]
185pub enum PluginError {
186 #[error("Couldn't create the plugin")]
187 CouldNotCreatePlugin,
188}