1use std::{collections::BTreeMap, marker::PhantomData};
2
3use anyhow::Result;
4use beetry_channel::{AnyBoxReceiver, AnyBoxSender};
5use beetry_core::{BoxActionBehavior, BoxConditionBehavior, BoxNode, NonEmptyNodes};
6use beetry_editor_types::{
7 output::node::{ParameterValue, Parameters},
8 spec::node::{NodeSpec, ParamsSpec, PortKey},
9};
10use bon::Builder;
11use serde::Deserialize;
12
13use crate::{BoxPlugin, ConstructPlugin, Named, PluginConstructor, PluginError, unique_plugins};
14
15pub type LeafReconstructionData = NodeReconstructionData<LeafContext>;
16pub type ActionReconstructionData = LeafReconstructionData;
17pub type ConditionReconstructionData = LeafReconstructionData;
18pub type ControlReconstructionData = NodeReconstructionData<ControlContext>;
19pub type DecoratorReconstructionData = NodeReconstructionData<DecoratorContext>;
20
21#[derive(Debug, Builder)]
22pub struct NodeReconstructionData<C> {
23 pub context: C,
24 #[builder(default)]
25 pub parameters: Parameters,
26}
27
28#[derive(Debug, Default, Builder)]
29pub struct LeafContext {
30 #[builder(default)]
31 pub receivers: BTreeMap<PortKey, AnyBoxReceiver>,
32 #[builder(default)]
33 pub senders: BTreeMap<PortKey, AnyBoxSender>,
34}
35
36impl LeafContext {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn take_receiver(&mut self, key: &PortKey) -> Result<AnyBoxReceiver> {
42 self.receivers
43 .remove(key)
44 .ok_or_else(|| anyhow::anyhow!("failed to obtain receiver for port '{}'", key.as_str()))
45 }
46
47 pub fn take_sender(&mut self, key: &PortKey) -> Result<AnyBoxSender> {
48 self.senders
49 .remove(key)
50 .ok_or_else(|| anyhow::anyhow!("failed to obtain sender for port '{}'", key.as_str()))
51 }
52}
53
54pub struct ControlContext {
55 pub children: NonEmptyNodes,
56}
57
58impl ControlContext {
59 pub fn new(children: NonEmptyNodes) -> Self {
60 Self { children }
61 }
62}
63
64pub struct DecoratorContext {
65 pub child: BoxNode,
66}
67
68impl DecoratorContext {
69 pub fn new(child: BoxNode) -> Self {
70 Self { child }
71 }
72}
73
74pub trait ProvideParamSpec {
111 fn provide() -> ParamsSpec;
112}
113
114pub struct ParamsDeserializer;
115
116impl ParamsDeserializer {
117 pub fn deserialize<T>(params: Parameters) -> Result<T>
118 where
119 T: for<'de> Deserialize<'de>,
120 {
121 let deserializer = serde_value::ValueDeserializer::<serde_value::DeserializerError>::new(
122 serde_value::Value::Map(
123 params
124 .into_iter()
125 .map(|(name, value)| {
126 let value = match value {
127 ParameterValue::Bool(b) => serde_value::Value::Bool(b),
128 ParameterValue::U16(u) => serde_value::Value::U16(u),
129 ParameterValue::U64(u) => serde_value::Value::U64(u),
130 ParameterValue::I64(i) => serde_value::Value::I64(i),
131 ParameterValue::F64(f) => serde_value::Value::F64(f),
132 ParameterValue::String(s) => serde_value::Value::String(s),
133 };
134 (serde_value::Value::String(name), value)
135 })
136 .collect::<BTreeMap<_, _>>(),
137 ),
138 );
139 Ok(T::deserialize(deserializer)?)
140 }
141}
142
143type BoxActionFactoryFn = Box<dyn Fn(ActionReconstructionData) -> Result<BoxActionBehavior>>;
144
145pub type ActionFactory = Factory<BoxActionFactoryFn, ActionReconstructionData, BoxActionBehavior>;
146pub type ConditionFactory =
147 Factory<BoxConditionFactoryFn, ConditionReconstructionData, BoxConditionBehavior>;
148type BoxConditionFactoryFn =
149 Box<dyn Fn(ConditionReconstructionData) -> Result<BoxConditionBehavior>>;
150
151type BoxControlFactoryFn = Box<dyn Fn(ControlReconstructionData) -> Result<BoxNode>>;
152pub type ControlFactory = Factory<BoxControlFactoryFn, ControlReconstructionData, BoxNode>;
153type BoxDecoratorFactoryFn = Box<dyn Fn(DecoratorReconstructionData) -> Result<BoxNode>>;
154pub type DecoratorFactory = Factory<BoxDecoratorFactoryFn, DecoratorReconstructionData, BoxNode>;
155
156pub struct Factory<F, I, O> {
157 func: F,
158 _ph1: PhantomData<I>,
159 _ph2: PhantomData<O>,
160}
161
162impl<F, I, O> Factory<F, I, O>
163where
166 F: Fn(I) -> Result<O>,
167{
168 pub fn new(func: F) -> Self {
169 Self {
170 func,
171 _ph1: PhantomData,
172 _ph2: PhantomData,
173 }
174 }
175
176 pub fn try_create(&self, data: I) -> Result<O> {
177 (self.func)(data)
178 }
179}
180pub type BoxActionPlugin = BoxPlugin<NodeSpec, ActionFactory>;
181pub type BoxConditionPlugin = BoxPlugin<NodeSpec, ConditionFactory>;
182pub type BoxControlPlugin = BoxPlugin<NodeSpec, ControlFactory>;
183pub type BoxDecoratorPlugin = BoxPlugin<NodeSpec, DecoratorFactory>;
184
185impl Named for NodeSpec {
186 fn name(&self) -> &str {
187 &self.name().0
188 }
189}
190
191pub type ActionPluginConstructor = PluginConstructor<NodeSpec, ActionFactory>;
192pub type ConditionPluginConstructor = PluginConstructor<NodeSpec, ConditionFactory>;
193pub type ControlPluginConstructor = PluginConstructor<NodeSpec, ControlFactory>;
194pub type DecoratorPluginConstructor = PluginConstructor<NodeSpec, DecoratorFactory>;
195
196impl ActionPluginConstructor {
197 pub fn plugins() -> Result<Vec<BoxActionPlugin>, PluginError> {
198 unique_plugins::<Self, <Self as ConstructPlugin>::Spec, <Self as ConstructPlugin>::Factory>(
199 )
200 }
201}
202
203impl ConditionPluginConstructor {
204 pub fn plugins() -> Result<Vec<BoxConditionPlugin>, PluginError> {
205 unique_plugins::<Self, <Self as ConstructPlugin>::Spec, <Self as ConstructPlugin>::Factory>(
206 )
207 }
208}
209
210impl ControlPluginConstructor {
211 pub fn plugins() -> Result<Vec<BoxControlPlugin>, PluginError> {
212 unique_plugins::<Self, <Self as ConstructPlugin>::Spec, <Self as ConstructPlugin>::Factory>(
213 )
214 }
215}
216
217impl DecoratorPluginConstructor {
218 pub fn plugins() -> Result<Vec<BoxDecoratorPlugin>, PluginError> {
219 unique_plugins::<Self, <Self as ConstructPlugin>::Spec, <Self as ConstructPlugin>::Factory>(
220 )
221 }
222}
223
224inventory::collect! {ActionPluginConstructor}
225inventory::collect! {ConditionPluginConstructor}
226inventory::collect! {ControlPluginConstructor}
227inventory::collect! {DecoratorPluginConstructor}
228
229#[cfg(test)]
230mod tests {
231 use beetry_editor_types::spec::node::{NodeKind, NodeName, NodeSpec, NodeSpecKey};
232
233 use super::*;
234 use crate::Plugin;
235
236 struct TestPluginA {
237 spec: NodeSpec,
238 factory: ActionFactory,
239 }
240
241 impl Plugin for TestPluginA {
242 type Spec = NodeSpec;
243 type Factory = ActionFactory;
244
245 fn new() -> Self {
246 Self {
247 spec: NodeSpec::builder()
248 .key(NodeSpecKey::new(
249 NodeName::new("TestPlugin"),
250 NodeKind::action(),
251 ))
252 .build(),
253 factory: ActionFactory::new(Box::new(|_| {
254 Err(anyhow::anyhow!("This is a test factory, not functional"))
255 })),
256 }
257 }
258
259 fn spec(&self) -> &Self::Spec {
260 &self.spec
261 }
262
263 fn factory(&self) -> &Self::Factory {
264 &self.factory
265 }
266
267 fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory) {
268 (self.spec, self.factory)
269 }
270 }
271
272 struct TestPluginB {
273 spec: NodeSpec,
274 factory: ActionFactory,
275 }
276
277 impl Plugin for TestPluginB {
278 type Spec = NodeSpec;
279 type Factory = ActionFactory;
280
281 fn new() -> Self {
282 Self {
283 spec: NodeSpec::builder()
284 .key(NodeSpecKey::new(
285 NodeName::new("TestPlugin"),
286 NodeKind::action(),
287 ))
288 .build(),
289 factory: ActionFactory::new(Box::new(|_| {
290 Err(anyhow::anyhow!("This is a test factory, not functional"))
291 })),
292 }
293 }
294
295 fn spec(&self) -> &Self::Spec {
296 &self.spec
297 }
298
299 fn factory(&self) -> &Self::Factory {
300 &self.factory
301 }
302
303 fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory) {
304 (self.spec, self.factory)
305 }
306 }
307
308 inventory::submit! {
309 ActionPluginConstructor::new::<TestPluginA>()
310 }
311
312 inventory::submit! {
316 ActionPluginConstructor::new::<TestPluginB>()
317 }
318
319 #[test]
320 fn duplicate_plugin_name_error() {
321 let result = ActionPluginConstructor::plugins();
322 assert!(matches!(
323 result,
324 Err(PluginError::DuplicateName(name)) if name == NodeName::new("TestPlugin").0
325 ));
326 }
327}