plux_rs/plugin.rs
1use std::{cmp::Ordering, fmt::Debug, sync::Arc};
2
3use semver::Version;
4
5use crate::{
6 Bundle, Depend, Info, Manager, PluginInfo, Registry,
7 function::Function,
8 utils::{PluginCallFunctionError, PluginCallRequestError, PluginRegisterFunctionError, Ptr},
9 variable::Variable,
10};
11
12/// Represents a loaded plugin instance.
13///
14/// A Plugin encapsulates all the information and functionality related to a single plugin,
15/// including its metadata, execution state, and available functions.
16///
17/// # Type Parameters
18///
19/// * `'a` - Lifetime parameter for references within the plugin
20/// * `O` - Output type for plugin functions (must implement Send + Sync)
21/// * `I` - Plugin information type (must implement Info trait)
22///
23/// # Fields
24///
25/// * `manager` - Reference to the manager responsible for this plugin
26/// * `info` - Plugin metadata and configuration
27/// * `is_load` - Whether the plugin is currently loaded and ready for execution
28/// * `requests` - Functions that this plugin must implement at the request of the host
29/// * `registry` - Functions exposed by this plugin to other plugins or the host
30pub struct Plugin<'a, O: Send + Sync, I: Info> {
31 pub(crate) manager: Ptr<'a, Box<dyn Manager<'a, O, I>>>,
32 pub(crate) info: PluginInfo<I>,
33 pub(crate) is_load: bool,
34 pub(crate) requests: Vec<Box<dyn Function<Output = O>>>,
35 pub(crate) registry: Registry<O>,
36}
37
38impl<'a, O: Send + Sync, I: Info> Plugin<'a, O, I> {
39 /// Creates a new plugin instance.
40 ///
41 /// This is an internal constructor used by the loader when registering plugins.
42 ///
43 /// # Parameters
44 ///
45 /// * `manager` - Reference to the manager responsible for this plugin
46 /// * `info` - Plugin metadata and configuration
47 ///
48 /// # Returns
49 ///
50 /// Returns a new Plugin instance with default unloaded state.
51 pub(crate) const fn new(
52 manager: Ptr<'a, Box<dyn Manager<'a, O, I>>>,
53 info: PluginInfo<I>,
54 ) -> Self {
55 Self {
56 manager,
57 info,
58 is_load: false,
59 requests: vec![],
60 registry: vec![],
61 }
62 }
63
64 /// Returns information about this plugin.
65 ///
66 /// # Returns
67 ///
68 /// Returns a reference to the plugin's metadata and configuration.
69 pub const fn info(&self) -> &PluginInfo<I> {
70 &self.info
71 }
72
73 /// Checks if the plugin is currently loaded and ready for execution.
74 ///
75 /// # Returns
76 ///
77 /// Returns `true` if the plugin is loaded, `false` otherwise.
78 pub const fn is_load(&self) -> bool {
79 self.is_load
80 }
81
82 /// Returns the list of function requests this plugin must implement.
83 ///
84 /// Function requests are functions that this plugin must implement at the request of the host.
85 /// These are functions that the host can call on this plugin when needed.
86 ///
87 /// # Returns
88 ///
89 /// Returns a reference to the vector of function requests.
90 pub const fn get_requests(&self) -> &Vec<Box<dyn Function<Output = O>>> {
91 &self.requests
92 }
93
94 /// Calls a function request by name with the given arguments.
95 ///
96 /// This method searches through the plugin's requests and executes the one matching
97 /// the provided name. These are functions that the plugin implements for the host to call.
98 ///
99 /// # Parameters
100 ///
101 /// * `name` - Name of the function request to call
102 /// * `args` - Arguments to pass to the function
103 ///
104 /// # Returns
105 ///
106 /// Returns `Result<O, PluginCallRequestError>` containing the function result on success,
107 /// or an error if the request is not found.
108 pub fn call_request(&self, name: &str, args: &[Variable]) -> Result<O, PluginCallRequestError> {
109 self.requests
110 .iter()
111 .find_map(|request| match request.name() == name {
112 true => Some(request.call(args)),
113 false => None,
114 })
115 .ok_or(PluginCallRequestError::NotFound)
116 }
117
118 /// Returns the registry of functions exposed by this plugin.
119 ///
120 /// The registry contains functions that this plugin makes available to other plugins
121 /// or the host application.
122 ///
123 /// # Returns
124 ///
125 /// Returns a reference to the function registry.
126 pub const fn get_registry(&self) -> &Registry<O> {
127 &self.registry
128 }
129
130 /// Registers a new function in this plugin's registry.
131 ///
132 /// This method adds a function to the plugin's registry, making it available for
133 /// other plugins or the host to call.
134 ///
135 /// # Parameters
136 ///
137 /// * `function` - The function to register
138 ///
139 /// # Returns
140 ///
141 /// Returns `Result<(), PluginRegisterFunctionError>` indicating success or failure.
142 /// Fails if a function with the same name is already registered.
143 pub fn register_function<F>(&mut self, function: F) -> Result<(), PluginRegisterFunctionError>
144 where
145 F: Function<Output = O> + 'static,
146 {
147 let find_function = self
148 .registry
149 .iter()
150 .find(|&f| f.as_ref() == &function as &dyn Function<Output = O>);
151
152 match find_function {
153 Some(_) => Err(PluginRegisterFunctionError::AlreadyExists(function.name())),
154 None => Ok(self.registry.push(Arc::new(function))),
155 }
156 }
157
158 /// Calls a function from this plugin's registry by name.
159 ///
160 /// This method searches through the plugin's registry and executes the function
161 /// matching the provided name.
162 ///
163 /// # Parameters
164 ///
165 /// * `name` - Name of the function to call
166 /// * `args` - Arguments to pass to the function
167 ///
168 /// # Returns
169 ///
170 /// Returns `Result<O, PluginCallFunctionError>` containing the function result on success,
171 /// or an error if the function is not found.
172 pub fn call_function(
173 &self,
174 name: &str,
175 args: &[Variable],
176 ) -> Result<O, PluginCallFunctionError> {
177 self.registry
178 .iter()
179 .find_map(|function| match function.name() == name {
180 true => Some(function.call(args)),
181 false => None,
182 })
183 .ok_or(PluginCallFunctionError::NotFound)
184 }
185}
186
187impl<O: Send + Sync, I: Info> PartialEq for Plugin<'_, O, I> {
188 fn eq(&self, other: &Self) -> bool {
189 self.info.bundle.id == other.info.bundle.id
190 && self.info.bundle.version == other.info.bundle.version
191 }
192}
193
194impl<O: Send + Sync, I: Info, ID: AsRef<str>> PartialEq<(ID, &Version)> for Plugin<'_, O, I> {
195 fn eq(&self, (id, version): &(ID, &Version)) -> bool {
196 self.info.bundle.id == *id.as_ref() && self.info.bundle.version == **version
197 }
198}
199
200impl<O: Send + Sync, I: Info> PartialEq<Bundle> for Plugin<'_, O, I> {
201 fn eq(&self, Bundle { id, version, .. }: &Bundle) -> bool {
202 self.info.bundle.id == *id && self.info.bundle.version == *version
203 }
204}
205
206impl<O: Send + Sync, I: Info> PartialEq<Depend> for Plugin<'_, O, I> {
207 fn eq(&self, Depend { id: name, version }: &Depend) -> bool {
208 self.info.bundle.id == *name && version.matches(&self.info.bundle.version)
209 }
210}
211
212impl<O: Send + Sync, I: Info> Eq for Plugin<'_, O, I> {}
213
214impl<O: Send + Sync, I: Info> PartialOrd for Plugin<'_, O, I> {
215 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216 match self.info.bundle.id == other.info.bundle.id {
217 true => self
218 .info
219 .bundle
220 .version
221 .partial_cmp(&other.info.bundle.version),
222 false => None,
223 }
224 }
225}
226
227impl<O: Send + Sync, I: Info, ID: AsRef<str>> PartialOrd<(ID, &Version)> for Plugin<'_, O, I> {
228 fn partial_cmp(&self, (id, version): &(ID, &Version)) -> Option<Ordering> {
229 match self.info.bundle.id == *id.as_ref() {
230 true => self.info.bundle.version.partial_cmp(*version),
231 false => None,
232 }
233 }
234}
235
236impl<O: Send + Sync, I: Info> PartialOrd<Bundle> for Plugin<'_, O, I> {
237 fn partial_cmp(&self, Bundle { id, version, .. }: &Bundle) -> Option<Ordering> {
238 match self.info.bundle.id == *id {
239 true => self.info.bundle.version.partial_cmp(version),
240 false => None,
241 }
242 }
243}
244
245impl<O: Send + Sync, I: Info> Debug for Plugin<'_, O, I> {
246 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247 f.debug_struct("Plugin")
248 .field("id", &self.info.bundle.id)
249 .field("version", &self.info.bundle.version)
250 .field("format", &self.info.bundle.format)
251 .field("path", &self.info.path)
252 .field("is_load", &self.is_load)
253 .field("depends", self.info.info.depends())
254 .field("optional_depends", self.info.info.optional_depends())
255 .finish()
256 }
257}