Skip to main content

beetry_plugin/
lib.rs

1//! # Beetry Plugin
2//!
3//! This crate is an internal Beetry implementation crate and is not considered
4//! part of the public API. For public APIs, use the `beetry` crate.
5//!
6//! This crate provides the plugin related functionality.
7
8mod channel;
9mod channel_macro;
10pub mod node;
11mod node_macro;
12
13pub use beetry_editor_types::spec::node::{
14    FieldDefinition, FieldMetadata, FieldTypeSpec, ParamsSpec,
15};
16pub use channel::{BoxChannelPlugin, ChannelPluginConstructor, Factory, TypeErasedChannel};
17
18pub use crate::node::{ParamsDeserializer, ProvideParamSpec};
19
20pub trait Plugin {
21    type Spec;
22    type Factory;
23
24    fn new() -> Self
25    where
26        Self: Sized;
27
28    fn spec(&self) -> &Self::Spec;
29
30    fn factory(&self) -> &Self::Factory;
31
32    fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory);
33}
34
35pub type BoxPlugin<S, F> = Box<dyn Plugin<Spec = S, Factory = F>>;
36
37impl<S, F> fmt::Debug for dyn Plugin<Spec = S, Factory = F> {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(
40            f,
41            "Plugin<Spec = {}, Factory = {}>",
42            std::any::type_name::<S>(),
43            std::any::type_name::<F>(),
44        )
45    }
46}
47
48pub trait Named {
49    fn name(&self) -> &str;
50}
51
52/// Internal helper trait to define unique plugins filtering
53trait ConstructPlugin {
54    type Spec: Named;
55    type Factory;
56    fn construct(&self) -> BoxPlugin<Self::Spec, Self::Factory>;
57}
58
59pub struct PluginConstructor<S, F>(pub fn() -> BoxPlugin<S, F>);
60
61impl<S, F> PluginConstructor<S, F>
62where
63    S: 'static,
64    F: 'static,
65{
66    #[must_use]
67    pub const fn new<P: Plugin<Spec = S, Factory = F> + 'static>() -> Self {
68        Self(|| Box::new(P::new()))
69    }
70}
71
72impl<S, F> ConstructPlugin for PluginConstructor<S, F>
73where
74    S: Named,
75{
76    type Factory = F;
77    type Spec = S;
78    fn construct(&self) -> BoxPlugin<Self::Spec, Self::Factory> {
79        (self.0)()
80    }
81}
82
83#[derive(Debug, Clone, thiserror::Error)]
84pub enum PluginError {
85    #[error("duplicate plugin name: '{0}'. Each plugin must have a unique name.")]
86    DuplicateName(String),
87}
88
89pub(crate) fn unique_plugins<C, S, F>() -> Result<Vec<BoxPlugin<S, F>>, PluginError>
90where
91    S: Named,
92    C: inventory::Collect + ConstructPlugin<Spec = S, Factory = F>,
93{
94    let mut seen_names = HashSet::new();
95
96    inventory::iter::<C>().try_fold(Vec::new(), |mut plugins, constructor| {
97        let plugin = constructor.construct();
98        let spec = plugin.spec();
99        let name = spec.name();
100
101        if seen_names.contains(name) {
102            return Err(PluginError::DuplicateName(name.into()));
103        }
104        seen_names.insert(name.to_string());
105
106        plugins.push(plugin);
107        Ok(plugins)
108    })
109}
110
111use std::{collections::HashSet, fmt};
112
113pub use inventory;
114
115#[macro_export]
116macro_rules! submit {
117    ($plugin:expr) => {
118        $crate::inventory::submit!($plugin);
119    };
120}
121
122#[doc(hidden)]
123pub mod __macro_support {
124    pub use anyhow;
125    pub use beetry_channel;
126    pub use beetry_core::{BoxActionBehavior, BoxConditionBehavior};
127    pub use beetry_editor_types::spec::{
128        channel::ChannelSpec,
129        node::{
130            NodeKind, NodeName, NodePortKind, NodePortSpec, NodeSpec, NodeSpecKey, PortKey,
131            PortsSpec,
132        },
133    };
134    pub use beetry_macros;
135    pub use beetry_message::MessageSpec;
136    pub use mitsein::iter1::FromIterator1;
137
138    pub use crate::node::{ActionReconstructionData, ConditionReconstructionData};
139}