1mod 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
52trait 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}