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}