Skip to main content

beetry_plugin/
node_macro.rs

1/// Creates an action plugin from Beetry's leaf-node DSL.
2///
3/// The generated plugin:
4///
5/// - defines a `NodeSpec`
6/// - defines optional port metadata and parameter metadata
7/// - defines a factory that reconstructs the action from reconstruction data
8/// - registers itself automatically
9///
10///
11/// Minimal action without params or channels:
12/// ```rust, no_run
13/// # use anyhow::Result;
14/// # use beetry_core::{ActionBehavior, ActionTask};
15/// # use beetry_plugin::action;
16/// struct WaitForSignal;
17///
18/// impl ActionBehavior for WaitForSignal {
19///     fn task(&mut self) -> Result<ActionTask> {
20///         # todo!()
21///     }
22/// }
23///
24/// action! {
25///     WaitForSignalPlugin: "Wait For Signal";
26///     create: WaitForSignal;
27/// }
28/// ```
29///
30/// Action with parameters:
31///
32/// Parameters are provided via `params(binding): ParamsType` syntax
33///
34/// Prerequisites: `ParamsType` is expected to implement
35/// [`crate::ProvideParamSpec`] and be deserializable
36///
37/// ```rust, no_run
38/// # use anyhow::Result;
39/// # use beetry_core::{ActionBehavior, ActionTask};
40/// # use beetry_editor_types::spec::node::{
41/// #     FieldDefinition, FieldMetadata, FieldTypeSpec, ParamsSpec,
42/// # };
43/// # use beetry_plugin::{ProvideParamSpec, action};
44/// # use mitsein::iter1::IntoIterator1;
45/// # use serde::Deserialize;
46/// #[derive(Deserialize)]
47/// struct RetryParams {
48///     retries: u64,
49/// }
50///
51/// impl ProvideParamSpec for RetryParams {
52///     fn provide() -> ParamsSpec {
53///         # [(
54///         #     "retries".into(),
55///         #     FieldDefinition {
56///         #         type_spec: FieldTypeSpec::U64(FieldMetadata::default()),
57///         #         description: Some("Maximum retry count".into()),
58///         #     },
59///         # )]
60///         # .into_iter1()
61///         # .collect1()
62///     }
63/// }
64///
65/// struct RetryAction {
66///     params: RetryParams,
67/// }
68///
69/// impl RetryAction {
70///     fn new(params: RetryParams) -> Self {
71///         # Self { params }
72///     }
73/// }
74///
75/// impl ActionBehavior for RetryAction {
76///     fn task(&mut self) -> Result<ActionTask> {
77///         # let _ = self.params.retries;
78///         # todo!()
79///     }
80/// }
81///
82/// action! {
83///     RetryActionPlugin: "Retry Action";
84///     params(parameters): RetryParams;
85///     create: RetryAction::new(parameters);
86/// }
87/// ```
88///
89/// Action with one receiver and one sender:
90///
91/// ```rust, no_run
92/// # use anyhow::Result;
93/// # use beetry_channel::{Receiver, Sender};
94/// # use beetry_core::{ActionBehavior, ActionTask};
95/// # use beetry_macros::Message;
96/// # use beetry_message::Message;
97/// # use beetry_message::type_hash::{self, TypeHash};
98/// # use beetry_plugin::action;
99/// #[derive(Debug, Clone, Copy, Default, TypeHash, Message)]
100/// struct Pose;
101///
102/// struct RelayPose<R, S>
103/// where
104///     R: Receiver<Pose>,
105///     S: Sender<Pose>,
106/// {
107///     input: R,
108///     output: S,
109/// }
110///
111/// impl<R, S> RelayPose<R, S>
112/// where
113///     R: Receiver<Pose>,
114///     S: Sender<Pose>,
115/// {
116///     fn new(input: R, output: S) -> Self {
117///         Self { input, output }
118///     }
119/// }
120///
121/// impl<R, S> ActionBehavior for RelayPose<R, S>
122/// where
123///     R: Receiver<Pose>,
124///     S: Sender<Pose>,
125/// {
126///     fn task(&mut self) -> Result<ActionTask> {
127///         # let _ = (&mut self.input, &mut self.output);
128///         # todo!()
129///     }
130/// }
131///
132/// action! {
133///     RelayPosePlugin: "Relay Pose";
134///     receivers: [input: Pose => "Incoming pose"];
135///     senders: [output: Pose => "Republished pose"];
136///     create: RelayPose::new(input, output);
137/// }
138/// ```
139/// The DSL supports both parameters and ports, so action nodes that depend on
140/// channels and parameters can be defined by combining the examples above.
141#[macro_export]
142macro_rules! action {
143    ($plugin_name:ident : $name:expr; $($tokens:tt)*) => {
144        $crate::__leaf_plugin_parse! {
145            ctx: {
146                plugin_name: $plugin_name,
147                node_name: $name,
148                factory_type: $crate::node::ActionFactory,
149                plugin_constructor: $crate::node::ActionPluginConstructor,
150                reconstruction_data: $crate::__macro_support::ActionReconstructionData,
151                behavior_box_type: $crate::__macro_support::BoxActionBehavior,
152                node_kind: $crate::__macro_support::NodeKind::action(),
153            },
154            receivers: [],
155            senders: [],
156            params: none,
157            params_binding: _parameters,
158            tokens: $($tokens)*
159        }
160    };
161}
162
163/// Creates a condition plugin from Beetry's leaf-node DSL.
164///
165/// Use when the node evaluates to success or failure without
166/// producing child nodes.
167///
168/// The DSL is the same as [`action!`]: `receivers`, `senders`, `params`, and
169/// `create` work the same way, but the constructed behavior must implement
170/// `ConditionBehavior` instead of `ActionBehavior`.
171#[macro_export]
172macro_rules! condition {
173    ($plugin_name:ident : $name:expr; $($tokens:tt)*) => {
174        $crate::__leaf_plugin_parse! {
175            ctx: {
176                plugin_name: $plugin_name,
177                node_name: $name,
178                factory_type: $crate::node::ConditionFactory,
179                plugin_constructor: $crate::node::ConditionPluginConstructor,
180                reconstruction_data: $crate::__macro_support::ConditionReconstructionData,
181                behavior_box_type: $crate::__macro_support::BoxConditionBehavior,
182                node_kind: $crate::__macro_support::NodeKind::condition(),
183            },
184            receivers: [],
185            senders: [],
186            params: none,
187            params_binding: _parameters,
188            tokens: $($tokens)*
189        }
190    };
191}
192
193/// Creates a decorator plugin for nodes that wrap a single child.
194///
195/// Minimal example with a bound child node:
196/// ```rust, no_run
197/// # use beetry_core::{Node, TickStatus};
198/// # use beetry_core::BoxNode;
199/// # use beetry_editor_types::spec::node::{NodeKind, NodeName, NodeSpec, NodeSpecKey};
200/// # use beetry_plugin::{
201/// #     Plugin,
202/// #     decorator,
203/// #     node::{DecoratorFactory, DecoratorPluginConstructor, DecoratorReconstructionData},
204/// # };
205/// struct Invert<N>
206/// {
207///     child: N,
208/// }
209///
210/// impl<N> Invert<N>
211/// where
212///     N: Node,
213/// {
214///     fn new(child: N) -> Self {
215///         Self { child }
216///     }
217/// }
218///
219/// impl<N> Node for Invert<N>
220/// where
221///     N: Node,
222/// {
223///     fn tick(&mut self) -> TickStatus {
224///     #    match self.child.tick() {
225///     #        TickStatus::Success => TickStatus::Failure,
226///     #        TickStatus::Failure => TickStatus::Success,
227///     #        TickStatus::Running => TickStatus::Running,
228///     #    }
229///     }
230/// }
231///
232/// decorator!(
233///     InvertPlugin: "Invert";
234///     child(child),
235///     create: Invert::new(child),
236/// );
237/// ```
238///
239/// Parameters can be attached the same way as in [`action!`].
240#[macro_export]
241macro_rules! decorator {
242    ($plugin_name:ident : $name:expr; child($child_binding:ident),create: $create:expr,) => {
243        $crate::__decorator_plugin_impl! {
244            $plugin_name,
245            $name,
246            $child_binding,
247            none,
248            _parameters,
249            $create
250        }
251    };
252    (
253        $plugin_name:ident :
254        $name:expr; child($child_binding:ident),params($params_binding:ident):
255        $params_ty:path,create:
256        $create:expr,
257    ) => {
258        $crate::__decorator_plugin_impl! {
259            $plugin_name,
260            $name,
261            $child_binding,
262            (typed $params_ty),
263            $params_binding,
264            $create
265        }
266    };
267}
268
269/// Creates a control plugin for nodes that manage multiple children.
270///
271///
272/// Minimal example with bound child nodes:
273/// ```rust, no_run
274/// # use beetry_core::{BoxNode, Node, NonEmptyNodes, TickStatus};
275/// # use beetry_editor_types::spec::node::{NodeKind, NodeName, NodeSpec, NodeSpecKey};
276/// # use beetry_plugin::{
277/// #     Plugin,
278/// #     control,
279/// #     node::{ControlFactory, ControlPluginConstructor, ControlReconstructionData},
280/// # };
281/// struct Sequence {
282///     children: NonEmptyNodes,
283/// }
284///
285/// impl Sequence {
286///     fn new(children: NonEmptyNodes) -> Self {
287///         Self { children }
288///     }
289/// }
290///
291/// impl Node for Sequence {
292///     fn tick(&mut self) -> TickStatus {
293///     #    for child in &mut self.children {
294///     #        match child.tick() {
295///     #            TickStatus::Success => continue,
296///     #            TickStatus::Failure => return TickStatus::Failure,
297///     #            TickStatus::Running => return TickStatus::Running,
298///     #        }
299///     #    }
300///     #
301///     #    TickStatus::Success
302///     }
303/// }
304///
305/// control!(
306///     SequencePlugin: "Sequence";
307///     children(children),
308///     create: Sequence::new(children),
309/// );
310/// ```
311///
312/// Parameters are attached the same way as in [`action!`].
313#[macro_export]
314macro_rules! control {
315    ($plugin_name:ident : $name:expr; children($children_binding:ident),create: $create:expr,) => {
316        $crate::__control_plugin_impl! {
317            $plugin_name,
318            $name,
319            $children_binding,
320            none,
321             _parameters,
322            $create
323        }
324    };
325    (
326        $plugin_name:ident :
327        $name:expr; children($children_binding:ident),params($params_binding:ident):
328        $params_ty:path,create:
329        $create:expr,
330    ) => {
331        $crate::__control_plugin_impl! {
332            $plugin_name,
333            $name,
334            $children_binding,
335            (typed $params_ty),
336            $params_binding,
337            $create
338        }
339    };
340}
341
342/// TT-muncher: parses plugin DSL tokens into
343/// receivers/senders/params/params_binding and a final `create` expression,
344/// then forwards parsed fields to the impl macro.
345#[doc(hidden)]
346#[macro_export]
347macro_rules! __leaf_plugin_parse {
348    (
349        ctx: $ctx:tt,
350        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
351        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
352        params: $params:tt,
353        params_binding: $params_binding:ident,
354        tokens: receivers: [$($new_receiver_name:ident : $new_receiver_ty:ty => $new_receiver_desc:literal),* $(,)?]; $($rest:tt)*
355    ) => {
356        $crate::__leaf_plugin_parse! {
357            ctx: $ctx,
358            receivers: [$($new_receiver_name : $new_receiver_ty => $new_receiver_desc),*],
359            senders: [$($sender_name : $sender_ty => $sender_desc),*],
360            params: $params,
361            params_binding: $params_binding,
362            tokens: $($rest)*
363        }
364    };
365    (
366        ctx: $ctx:tt,
367        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
368        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
369        params: $params:tt,
370        params_binding: $params_binding:ident,
371        tokens: senders: [$($new_sender_name:ident : $new_sender_ty:ty => $new_sender_desc:literal),* $(,)?]; $($rest:tt)*
372    ) => {
373        $crate::__leaf_plugin_parse! {
374            ctx: $ctx,
375            receivers: [$($receiver_name : $receiver_ty => $receiver_desc),*],
376            senders: [$($new_sender_name : $new_sender_ty => $new_sender_desc),*],
377            params: $params,
378            params_binding: $params_binding,
379            tokens: $($rest)*
380        }
381    };
382    (
383        ctx: $ctx:tt,
384        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
385        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
386        params: $params:tt,
387        params_binding: $params_binding:ident,
388        tokens: params($new_params_binding:ident): $new_params_ty:path; $($rest:tt)*
389    ) => {
390        $crate::__leaf_plugin_parse! {
391            ctx: $ctx,
392            receivers: [$($receiver_name : $receiver_ty => $receiver_desc),*],
393            senders: [$($sender_name : $sender_ty => $sender_desc),*],
394            params: (typed $new_params_ty),
395            params_binding: $new_params_binding,
396            tokens: $($rest)*
397        }
398    };
399    (
400        ctx: $ctx:tt,
401        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
402        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
403        params: $params:tt,
404        params_binding: $params_binding:ident,
405        tokens: create: $create:expr $(;)?
406    ) => {
407        $crate::__leaf_plugin_impl! {
408            ctx: $ctx,
409            receivers: [$($receiver_name : $receiver_ty => $receiver_desc),*],
410            senders: [$($sender_name : $sender_ty => $sender_desc),*],
411            params: $params,
412            params_binding: $params_binding,
413            create: $create
414        }
415    };
416    (
417        ctx: $ctx:tt,
418        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
419        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
420        params: $params:tt,
421        params_binding: $params_binding:ident,
422        tokens: $($unexpected:tt)+
423    ) => {
424        compile_error!(
425            "invalid plugin DSL. Expected fields separated by ';': \
426             receivers: [...]; senders: [...]; params(<ident>): <type-path>; create: <expr>;"
427        );
428    };
429}
430
431#[doc(hidden)]
432#[macro_export]
433macro_rules! __leaf_plugin_extract_receivers {
434    ([], $data:ident) => {};
435    ([$($port_name:ident : $port_ty:ty => $port_desc:literal),+], $data:ident) => {
436        $(
437            let $port_name = $data
438                .context
439                .take_receiver(&$crate::__macro_support::PortKey::new(stringify!($port_name)))?
440                .into_receiver_of::<$port_ty>()
441                .map_err(|_| {
442                    $crate::__macro_support::anyhow::anyhow!(
443                        concat!(
444                            "failed to obtain typed receiver for port '",
445                            stringify!($port_name),
446                            "'"
447                        )
448                    )
449                })?;
450        )+
451    };
452}
453
454#[doc(hidden)]
455#[macro_export]
456macro_rules! __leaf_plugin_extract_senders {
457    ([], $data:ident) => {};
458    ([$($port_name:ident : $port_ty:ty => $port_desc:literal),+], $data:ident) => {
459        $(
460            let $port_name = $data
461                .context
462                .take_sender(&$crate::__macro_support::PortKey::new(stringify!($port_name)))?
463                .into_sender_of::<$port_ty>()
464                .map_err(|_| {
465                    $crate::__macro_support::anyhow::anyhow!(
466                        concat!(
467                            "failed to obtain typed sender for port '",
468                            stringify!($port_name),
469                            "'"
470                        )
471                    )
472                })?;
473        )+
474    };
475}
476
477#[doc(hidden)]
478#[macro_export]
479macro_rules! __leaf_plugin_build_ports {
480    ([], []) => {
481        None
482    };
483    ([$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),+], []) => {
484        Some(
485            <$crate::__macro_support::PortsSpec as $crate::__macro_support::FromIterator1<
486                $crate::__macro_support::NodePortSpec,
487            >>::from_iter1([
488                $(
489                    $crate::__macro_support::NodePortSpec {
490                        key: $crate::__macro_support::PortKey::new(stringify!($receiver_name)),
491                        kind: $crate::__macro_support::NodePortKind::Receiver,
492                        msg_spec: $crate::__macro_support::MessageSpec::new::<$receiver_ty>($receiver_desc),
493                    }
494                ),+
495            ]),
496        )
497    };
498    ([], [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),+]) => {
499        Some(
500            <$crate::__macro_support::PortsSpec as $crate::__macro_support::FromIterator1<
501                $crate::__macro_support::NodePortSpec,
502            >>::from_iter1([
503                $(
504                    $crate::__macro_support::NodePortSpec {
505                        key: $crate::__macro_support::PortKey::new(stringify!($sender_name)),
506                        kind: $crate::__macro_support::NodePortKind::Sender,
507                        msg_spec: $crate::__macro_support::MessageSpec::new::<$sender_ty>($sender_desc),
508                    }
509                ),+
510            ]),
511        )
512    };
513    ([$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),+], [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),+]) => {
514        Some(
515            <$crate::__macro_support::PortsSpec as $crate::__macro_support::FromIterator1<
516                $crate::__macro_support::NodePortSpec,
517            >>::from_iter1([
518                $(
519                    $crate::__macro_support::NodePortSpec {
520                        key: $crate::__macro_support::PortKey::new(stringify!($receiver_name)),
521                        kind: $crate::__macro_support::NodePortKind::Receiver,
522                        msg_spec: $crate::__macro_support::MessageSpec::new::<$receiver_ty>($receiver_desc),
523                    }
524                ),+,
525                $(
526                    $crate::__macro_support::NodePortSpec {
527                        key: $crate::__macro_support::PortKey::new(stringify!($sender_name)),
528                        kind: $crate::__macro_support::NodePortKind::Sender,
529                        msg_spec: $crate::__macro_support::MessageSpec::new::<$sender_ty>($sender_desc),
530                    }
531                ),+
532            ]),
533        )
534    };
535}
536
537#[doc(hidden)]
538#[macro_export]
539macro_rules! __optional_params_spec {
540    (none) => {
541        None
542    };
543    ((typed $params_ty:path)) => {
544        Some(<$params_ty as $crate::ProvideParamSpec>::provide())
545    };
546}
547
548#[doc(hidden)]
549#[macro_export]
550macro_rules! __deserialize_params {
551    (none, $params_binding:ident, $parameters:expr) => {};
552    ((typed $params_ty:path), $params_binding:ident, $parameters:expr) => {
553        let $params_binding = $crate::ParamsDeserializer::deserialize::<$params_ty>($parameters)?;
554    };
555}
556
557#[doc(hidden)]
558#[macro_export]
559macro_rules! __leaf_plugin_impl {
560    (
561        ctx: {
562            plugin_name: $plugin_name:ident,
563            node_name: $name:expr,
564            factory_type: $factory_type:ty,
565            plugin_constructor: $plugin_constructor:ty,
566            reconstruction_data: $reconstruction_data:ty,
567            behavior_box_type: $behavior_box_type:ty,
568            node_kind: $node_kind:expr,
569        },
570        receivers: [$($receiver_name:ident : $receiver_ty:ty => $receiver_desc:literal),* $(,)?],
571        senders: [$($sender_name:ident : $sender_ty:ty => $sender_desc:literal),* $(,)?],
572        params: $params:tt,
573        params_binding: $params_binding:ident,
574        create: $create:expr $(,)?
575    ) => {
576        pub struct $plugin_name {
577            spec: $crate::__macro_support::NodeSpec,
578            factory: $factory_type,
579        }
580
581        impl $crate::Plugin for $plugin_name {
582            type Spec = $crate::__macro_support::NodeSpec;
583            type Factory = $factory_type;
584
585            fn new() -> Self
586            where
587                Self: Sized,
588            {
589                let factory_fn = |mut data: $reconstruction_data| {
590                    $crate::__leaf_plugin_extract_receivers!([$($receiver_name : $receiver_ty => $receiver_desc),*], data);
591                    $crate::__leaf_plugin_extract_senders!([$($sender_name : $sender_ty => $sender_desc),*], data);
592                    $crate::__deserialize_params!($params, $params_binding, data.parameters);
593                    Ok(Box::new($create) as $behavior_box_type)
594                };
595
596                let spec = $crate::__macro_support::NodeSpec::builder()
597                    .key($crate::__macro_support::NodeSpecKey::new(
598                        $crate::__macro_support::NodeName::new($name),
599                        $node_kind,
600                    ))
601                    .maybe_ports($crate::__leaf_plugin_build_ports!(
602                        [$($receiver_name : $receiver_ty => $receiver_desc),*],
603                        [$($sender_name : $sender_ty => $sender_desc),*]
604                    ))
605                    .maybe_params($crate::__optional_params_spec!($params))
606                    .build();
607
608                Self {
609                    spec,
610                    factory: Self::Factory::new(Box::new(factory_fn)),
611                }
612            }
613
614            fn spec(&self) -> &Self::Spec {
615                &self.spec
616            }
617
618            fn factory(&self) -> &Self::Factory {
619                &self.factory
620            }
621
622            fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory) {
623                (self.spec, self.factory)
624            }
625        }
626
627        $crate::submit!(<$plugin_constructor>::new::<$plugin_name>());
628    };
629}
630
631#[doc(hidden)]
632#[macro_export]
633macro_rules! __control_plugin_impl {
634    (
635        $plugin_name:ident,
636        $name:expr,
637        $children_binding:ident,
638        $params:tt,
639        $params_binding:ident,
640        $create:expr $(,)?
641    ) => {
642        struct $plugin_name {
643            spec: NodeSpec,
644            factory: ControlFactory,
645        }
646
647        impl Plugin for $plugin_name {
648            type Spec = NodeSpec;
649            type Factory = ControlFactory;
650
651            fn new() -> Self
652            where
653                Self: Sized,
654            {
655                Self {
656                    spec: NodeSpec::builder()
657                        .key(NodeSpecKey::new(NodeName::new($name), NodeKind::Control))
658                        .maybe_params($crate::__optional_params_spec!($params))
659                        .build(),
660
661                    factory: ControlFactory::new(Box::new(|data: ControlReconstructionData| {
662                        $crate::__deserialize_params!($params, $params_binding, data.parameters);
663                        let $children_binding = data.context.children;
664
665                        Ok(Box::new($create) as BoxNode)
666                    })),
667                }
668            }
669
670            fn spec(&self) -> &Self::Spec {
671                &self.spec
672            }
673
674            fn factory(&self) -> &Self::Factory {
675                &self.factory
676            }
677
678            fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory) {
679                (self.spec, self.factory)
680            }
681        }
682
683        $crate::submit!(ControlPluginConstructor::new::<$plugin_name>());
684    };
685}
686
687#[doc(hidden)]
688#[macro_export]
689macro_rules! __decorator_plugin_impl {
690    (
691        $plugin_name:ident,
692        $name:expr,
693        $child_binding:ident,
694        $params:tt,
695        $params_binding:ident,
696        $create:expr $(,)?
697    ) => {
698        struct $plugin_name {
699            spec: NodeSpec,
700            factory: DecoratorFactory,
701        }
702
703        impl Plugin for $plugin_name {
704            type Spec = NodeSpec;
705            type Factory = DecoratorFactory;
706
707            fn new() -> Self
708            where
709                Self: Sized,
710            {
711                Self {
712                    spec: NodeSpec::builder()
713                        .key(NodeSpecKey::new(NodeName::new($name), NodeKind::Decorator))
714                        .maybe_params($crate::__optional_params_spec!($params))
715                        .build(),
716
717                    factory: DecoratorFactory::new(Box::new(
718                        |data: DecoratorReconstructionData| {
719                            $crate::__deserialize_params!(
720                                $params,
721                                $params_binding,
722                                data.parameters
723                            );
724                            let $child_binding = data.context.child;
725
726                            Ok(Box::new($create) as BoxNode)
727                        },
728                    )),
729                }
730            }
731
732            fn spec(&self) -> &Self::Spec {
733                &self.spec
734            }
735
736            fn factory(&self) -> &Self::Factory {
737                &self.factory
738            }
739
740            fn into_parts(self: Box<Self>) -> (Self::Spec, Self::Factory) {
741                (self.spec, self.factory)
742            }
743        }
744
745        $crate::submit!(DecoratorPluginConstructor::new::<$plugin_name>());
746    };
747}