frame_suite/plugins.rs
1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// ```````````````````````````````` PLUGINS SUITE ````````````````````````````````
14// ===============================================================================
15
16//! Pluggable, type-safe execution framework for composing runtime behavior via plugin
17//! models and families.
18//!
19//! This module offers two complementary abstractions for extensible, type-safe
20//! runtime behaviour:
21//!
22//! - Read [Plugin Model](#plugin-model) to understand the **fundamental unit**
23//! of computation - a single operation plugin, analogous to a pure function
24//! or a procedure that may mutate its input and produce an output.
25//!
26//! - Read [Plugin Families](#plugin-families) when modelling a **cohesive
27//! state-machine-like component** composed of multiple related operations
28//! (methods). A family groups several operation-specific plugin models under
29//! one logical root, while the concrete implementation variant of each
30//! operation is plugged via a single family model.
31//!
32//! > **Note:** Plugin families are built on top of plugin models.
33//! > To correctly design or use families, one must first understand the
34//! > plugin model abstraction, since families internally orchestrate multiple
35//! > models as their operational building blocks.
36//!
37//! In short:
38//!
39//! ```text
40//! Simple, single-step transformation? -> Plugin Model
41//! Multi-operation logical component / state machine? -> Plugin Family (built from Plugin Models)
42//! ```
43//!
44//! Both approaches share the same execution infrastructure and compile-time
45//! type-safety guarantees, but differ in how behaviour is structured,
46//! composed, and ultimately resolved.
47//!
48//! # Plugin Model
49//!
50//! A **plugin model** is a type-safe, swappable unit of computation with optional
51//! context. It enables deterministic, composable, and runtime-configurable behavior.
52//!
53//! Each model:
54//! - Implements [`PurePluginModel<Input, Context, Output>`] or
55//! [`MutablePluginModel<Input, Context, Output>`]
56//! - Produces an output from input (and optional context)
57//!
58//! Execution:
59//! - `compute(input, &context) -> output`
60//! - `compute_mut(&mut input, &context) -> output`
61//!
62//! Context:
63//! - Provided via [`ModelContext`]
64//! - Defined using [`plugin_context`](crate::plugin_context)
65//!
66//! Tooling:
67//! - Declare via [`plugin_types`](crate::plugin_types)
68//! - Define via [`plugin_model`](crate::plugin_model)
69//! - Execute via [`plugin_output`](crate::plugin_output)
70//! - Test with [`plugin_test`](crate::plugin_test)
71//!
72//! ## Motivation
73//!
74//! Conventional trait-based designs couple the **contract** and the
75//! **implementation** into a single resolution step:
76//!
77//! ```text
78//! Sub-set Contract (Trait Bounds)
79//! |
80//! v
81//! Concrete Type (Implementation)
82//! ```
83//!
84//! The implementation is chosen first, and the contract is something it must
85//! satisfy. Any **stronger bounds or richer behavior** remain internal to the
86//! concrete type and cannot be independently selected or composed.
87//!
88//! This leads to key limitations:
89//!
90//! - Behavior is fixed once the type is chosen
91//! - Stronger capabilities cannot be surfaced or selected explicitly
92//! - Context-driven or configuration-based behavior becomes difficult
93//!
94//! ## Behaviour Model
95//!
96//! Plugin models treat behavior as a **compatibility problem between two
97//! independent entities**:
98//!
99//! ```text
100//! Sub-set Contract (Pallet)
101//! <->
102//! Super-set Capability (Model + Bounds + Context)
103//! ```
104//!
105//! These are defined independently and only come together through
106//! **compatibility matching**.
107//!
108//! Matching rule:
109//!
110//! - The super-set must satisfy all requirements of the sub-set
111//! - The sub-set must allow the super-set's stronger bounds
112//!
113//! Only when both conditions hold does a valid composition exist.
114//!
115//! ## Benefits
116//!
117//! - Decoupled contract and behavior
118//! - Multiple interchangeable implementations
119//! - Context-driven execution
120//! - Late selection via configuration
121//! - Full compile-time verification
122//!
123//! Behaviour is not implemented, it is resolved by matching a sub-set contract
124//! with a compatible super-set capability.
125//!
126//! ## Example: Sorter Plugin
127//!
128//! This example shows how a pallet can dynamically select sorting strategies
129//! at runtime via plugin types.
130//!
131//! ```ignore
132//!
133//! // ----- Support Crate -------
134//!
135//! /// A generic sorter plugin trait.
136//! ///
137//! /// This trait defines a plugin point where the actual sorting logic
138//! /// is provided by an associated plugin model and its context.
139//! pub trait Sorter<Input> {
140//! /// The output type produced by the sorter.
141//! type Output;
142//!
143//! // Declare the associated plugin model and context types.
144//! // These will be supplied by downstream crates (e.g., pallets or runtime).
145//! plugin_types! {
146//! input: Input,
147//! output: Self::Output,
148//! model: Model,
149//! context: Context,
150//! }
151//!
152//! plugin_output! {
153//! /// Execute the sorting logic using the injected plugin model.
154//! ///
155//! /// The actual implementation is resolved at compile time based on
156//! /// the associated `Model` and `Context` types.
157//! fn sort
158//! input: values,
159//! model: Self::Model,
160//! context: Self::Context,
161//! }
162//! }
163//!
164//! // ----- Pallet Crate -------
165//!
166//! /// Pallet configuration exposing plugin hook points.
167//! ///
168//! /// The runtime will decide which concrete model and context to use.
169//! pub trait Config: frame_system::Config {
170//! /// Input type consumed by the sorter plugin.
171//! type InputX;
172//!
173//! /// Output type produced by the sorter plugin.
174//! type OutputX;
175//!
176//! // Declare pallet-level plugin types that must satisfy the plugin contract.
177//! plugin_types! {
178//! input: Self::InputX,
179//! output: Self::OutputX,
180//! model: ModelX, // Concrete model chosen by the runtime
181//! context: ContextX, // Concrete context provider chosen by the runtime
182//! }
183//! }
184//!
185//! /// Implement the generic sorter plugin for the pallet.
186//! ///
187//! /// The pallet simply forwards execution to the configured model.
188//! impl<T: Config> Sorter<T::InputX> for Pallet<T> {
189//! type Output = T::OutputX;
190//! type Model = T::ModelX;
191//! type Context = T::ContextX;
192//! }
193//!
194//! /// Helper function demonstrating how the plugin is executed generically.
195//! fn try_sort<T: Config>(values: &T::InputX) -> T::OutputX {
196//! <Pallet<T> as Sorter<T::InputX>>::sort(values)
197//! }
198//!
199//! // ----- Runtime Crate -------
200//!
201//! /// Define a generic plugin model.
202//! /// This model sorts the generic type `Vector` in ascending order and then
203//! /// purges all elements greater than the runtime `until` threshold.
204//! /// Such many models like this can live in plugin-registries
205//! plugin_model! {
206//! name: CappedSort,
207//! input: Vector,
208//! output: Vector,
209//! context: UntilConfig<Number>,
210//! others: [Number],
211//! bounds: [
212//! // Elements must be comparable and clonable
213//! Number: Unsigned + Clone + Ord,
214//! // Vector must be iterable and rebuildable after filtering
215//! Vector: IntoIterator<Item = Number> + FromIterator<Number> + Clone,
216//! ],
217//! compute: |values, ctx| {
218//! // Clone input so original remains unchanged
219//! let mut v: Vector = values.clone();
220//!
221//! // Retrieve runtime threshold from context
222//! let until = ctx.0.clone();
223//!
224//! // Sort from small to large
225//! let mut temp: Vec<Number> = v.into_iter().collect();
226//! temp.sort();
227//!
228//! // Find first element greater than `until`
229//! // Purge that element and everything after it
230//! let filtered = temp
231//! .into_iter()
232//! .take_while(|x| *x <= until)
233//! .collect::<Vec<_>>();
234//!
235//! // Rebuild the output vector from the filtered values
236//! filtered.into_iter().collect()
237//! }
238//! }
239//!
240//! /// Context data structure holding the threshold value.
241//! /// Such many models's contexts like this can live in
242//! /// plugin-registries, as models and its contexts are tightly coupled
243//! struct UntilConfig<Number>(Number);
244//!
245//! /// Define a concrete context provider supplying the threshold.
246//! plugin_context! {
247//! name: MyContext,
248//! context: UntilConfig<u8>,
249//! value: UntilConfig(10),
250//! }
251//!
252//! /// Inject the concrete model and context into the runtime configuration.
253//! impl Config for Runtime {
254//! type InputX = Vec<u8>;
255//! type OutputX = Vec<u8>;
256//! type ModelX = CappedSort; // Uses capped sorting logic
257//! type ContextX = MyContext; // Provides the `until` threshold
258//! }
259//!
260//! // Example behavior:
261//! // Input: vec![12, 3, 8, 25, 5]
262//! // Sorted: [3, 5, 8, 12, 25]
263//! // until = 10
264//! // Output: [3, 5, 8] // elements > 10 are purged
265//!
266//! ```
267//!
268//! The pallet only assumes a generic "sorter" transformation. The runtime injects
269//! `CappedSort`, which sorts values ascending and purges elements greater than a
270//! contextual `until` threshold. The pallet sees only the minimal contract,
271//! while the runtime provides richer, context-driven logic.
272//!
273//! # Plugin Families
274//!
275//! A **plugin family** extends a single plugin model into a **unified logical
276//! plugin** with multiple related operations.
277//!
278//! Unlike a model (one computation), a family represents a cohesive component
279//! (e.g., state machine or service) whose operations are selected via *child*
280//! markers, while a **family type** maps them to concrete models.
281//!
282//! This lets callers use a single interface while deferring implementation
283//! choice to runtime configuration.
284//!
285//! ## Motivation
286//!
287//! A single model fits simple transformations:
288//!
289//! ```text
290//! Model -> Implementation
291//! Context -> Parameters
292//! ```
293//!
294//! But real systems need:
295//!
296//! - Multiple related operations
297//! - Multiple strategy variants
298//! - Configurable behaviour
299//!
300//! A **plugin family** groups these operations under one unit, where:
301//!
302//! - Children = operations
303//! - Family type = model mapping
304//!
305//! Result: structured, state-machine-like design with compile-time resolution.
306//!
307//! ## State-Machine Style Logical Plugin
308//!
309//! A plugin family acts as a logical component exposing multiple operations,
310//! similar to a state machine or service:
311//!
312//! ```text
313//! Family Root (Unified Interface)
314//! |-- Child A -> Operation A
315//! |-- Child B -> Operation B
316//! |-- Child C -> Operation C -> Concrete Model (via Family Type)
317//! ```
318//!
319//! - **Root**: unified special-interface
320//! - **Child**: operation selector
321//! - **Family type**: maps each operation to a concrete model
322//!
323//! Calling a child is equivalent to invoking a method on the plugin.
324//! The concrete model is resolved by the family type, with context passed
325//! through to execution.
326//!
327//! ### Family Contract Consistency
328//!
329//! All models within a **family type** are expected to share the same
330//! execution contract:
331//!
332//! ```text
333//! Input -> shared
334//! Output -> shared
335//! Context -> shared
336//! ```
337//!
338//! This allows the family to behave as a **uniform pluggable component**,
339//! where operations can be invoked without knowledge of the underlying model.
340//!
341//! ```text
342//! Family Type
343//! |-- Child A -> ModelA<Input, Context, Output>
344//! |-- Child B -> ModelB<Input, Context, Output>
345//! |-- Child C -> ModelC<Input, Context, Output>
346//! ```
347//!
348//! With a consistent `(Input, Output, Context)` signature, callers interact
349//! through the root interface while the compiler resolves the concrete model.
350//!
351//! While not strictly enforced, consistent contracts are recommended for
352//! clarity and interchangeability.
353//!
354//! ### Trait Bound Consistency
355//!
356//! Plugin models define `(Input, Output, Context)` generically via trait bounds.
357//!
358//! Within a **family type**, the concrete types must satisfy the **combined
359//! bounds** of all possible models.
360//!
361//! ```text
362//! ModelA requires: Input: Ord
363//! ModelB requires: Input: Clone
364//! ```
365//!
366//! -> Caller must provide:
367//!
368//! ```text
369//! Input: Ord + Clone
370//! ```
371//!
372//! The family contract therefore reflects the **union of required bounds**:
373//!
374//! ```text
375//! Family Contract
376//! Input: Ord + Clone
377//! Output: ...
378//! ```
379//!
380//! This guarantees that any selected model can be resolved safely at compile time.
381//!
382//! ## Plugin Family as a Logical Plugin State Machine
383//!
384//! ```text
385//! +---------------------------------------+
386//! | FAMILY ROOT |
387//! | Unified logical plugin interface |
388//! +--------------------+------------------+
389//! |
390//! Concrete Family Type
391//! |
392//! +-----------------------------------+-----------------------------------+
393//! | | |
394//! +--------------+ +--------------+ +--------------+
395//! | Child A | | Child B | | Child C |
396//! | Operation A | | Operation B | | Operation C |
397//! +------+-------+ +------+-------+ +------+-------+
398//! | | |
399//! +------+-------+ +------+-------+ +------+-------+
400//! | Model A | | Model B | | Model C |
401//! | selected by | | selected by | | selected by |
402//! | Family Type | | Family Type | | Family Type |
403//! +------+-------+ +------+-------+ +------+-------+
404//! | | |
405//! +---------------------------- Context ----------------------------------+
406//! (shared across models)
407//! ```
408//!
409//! In this structure:
410//!
411//! - The **family root** represents the unified logical plugin interface.
412//! - Each **child marker** represents one operation of that interface.
413//! - The **family type** determines which concrete model implements each
414//! operation.
415//!
416//! All models belonging to the same family share the **same context type**.
417//! The context is therefore represented as a single input flowing into the
418//! resolved model during execution.
419//!
420//! ### Resolution Flow
421//!
422//! ```text
423//! Caller invokes:
424//! FamilyRoot + FamilyType + ChildX
425//!
426//! Compiler resolves:
427//! (FamilyType, ChildX) -> ConcreteModel
428//!
429//! Execution:
430//! ConcreteModel.compute(input, context)
431//! ```
432//!
433//! This allows callers to treat the family as a single logical plugin while
434//! the compiler statically resolves the concrete model for each operation.
435//!
436//! ## Declaration Model
437//!
438//! A plugin family is constructed using three complementary macros:
439//!
440//! - [`declare_family`](crate::declare_family)
441//! - [`plugin_model`](crate::plugin_model)
442//! - [`define_family`](crate::define_family)
443//!
444//! Together they define the **operations**, **models**, and **family
445//! implementation** that make up a plugin family.
446//!
447//! ### 1. Declaring the Family Interface
448//!
449//! The [`declare_family`](crate::declare_family) macro defines the **family
450//! root trait** and a set of **child marker types** representing operations
451//! of the logical plugin.
452//!
453//! ```text
454//! Family Root
455//! |-- ChildA
456//! |-- ChildB
457//! |-- ChildC
458//! ```
459//!
460//! The root trait represents the unified plugin interface, while each child
461//! marker identifies one operation that the family exposes.
462//!
463//!
464//! ### 2. Defining Plugin Models
465//!
466//! Concrete behaviour is implemented using [`plugin_model`](crate::plugin_model).
467//!
468//! Each plugin model implements a specific `(Input, Context, Output)`
469//! computation and can later be attached to a family operation.
470//!
471//! ```text
472//! ModelA<Input, Context, Output>
473//! ModelB<Input, Context, Output>
474//! ModelC<Input, Context, Output>
475//! ```
476//!
477//! Models remain independent units of computation and can be reused across
478//! different families.
479//!
480//!
481//! ### 3. Defining the Family Implementation
482//!
483//! The [`define_family`](crate::define_family) macro creates a **concrete
484//! family type** that binds each child operation to a specific model.
485//!
486//! ```text
487//! FamilyType
488//! |-- ChildA -> ModelA
489//! |-- ChildB -> ModelB
490//! |-- ChildC -> ModelC
491//! ```
492//!
493//! This family type represents a concrete implementation of the family root
494//! and determines which models are used for each operation.
495//!
496//!
497//! ### Resolution Model
498//!
499//! When a caller invokes an operation, it refers only to the **family root**
500//! and a **child marker**.
501//!
502//! The compiler then resolves the concrete model using the configured
503//! family type:
504//!
505//! ```text
506//! (FamilyType, Child) -> ConcreteModel
507//! ```
508//!
509//! The resolved model is then executed using the provided `(Input, Context)`
510//! values.
511//!
512//! This design allows callers to interact with the family as a single logical
513//! plugin while the runtime configuration determines the concrete behaviour
514//! through the selected **family type**.
515//!
516//!
517//! ## Immutable vs Mutable Operational Variants
518//!
519//! A family may host immutable (`PurePluginModel`) and/or mutable
520//! (`MutablePluginModel`) variants for its operations. However, mutability forms
521//! part of the execution contract:
522//!
523//! - Immutable execution resolves only to pure models.
524//! - Mutable execution resolves only to mutable models.
525//!
526//! Even if both coexist in the same family hierarchy, they are not
527//! interchangeable at the usage site because the caller's expected execution
528//! semantics are part of the type-level interface.
529//!
530//! ## Example: Family-Based Model Resolution
531//!
532//! This example demonstrates how a plugin **family** defines a semantic
533//! extension point on a caller trait and how concrete models attach
534//! themselves to that family. The runtime then selects the active model
535//! by supplying an appropriate context.
536//!
537//! In this design the **family is declared by the caller trait**, because
538//! the trait owns the extension point. Concrete plugin models merely
539//! register themselves under that family.
540//!
541//! ### Caller Trait - Declaring the Plugin Family
542//!
543//! The caller trait defines the **plugin contract** and declares the family
544//! that models may attach to.
545//!
546//! ```ignore
547//! declare_family! {
548//! root: pub MathFamilyRoot,
549//! child: [MaybePlusOne]
550//! }
551//!
552//! pub trait MathTrait {
553//! type Input: AtLeast8BitUnsigned;
554//! type Output: AtLeast8BitUnsigned;
555//!
556//! plugin_types! {
557//! input: Self::Input,
558//! output: Self::Output,
559//! root: MathFamilyRoot,
560//! family: MathFamily
561//! context: MathContext,
562//! }
563//!
564//! plugin_output! {
565//! fn request,
566//! input: Self::Input,
567//! output: Self::Output,
568//! root: MathFamilyRoot,
569//! family: Self::MathFamily
570//! child: MaybePlusOne,
571//! context: Self::MathContext,
572//! }
573//! }
574//! ```
575//!
576//! Here:
577//!
578//! - `MathFamily` defines the semantic plugin domain.
579//! - `MaybePlusOne` acts as a **child selector**, representing an optional
580//! increment strategy.
581//!
582//! The trait itself does **not specify which model is used**.
583//!
584//! ### Plugin Models - Registering Implementations
585//!
586//! Plugin models implement behavior for a specific `(Family, Child, Context)`
587//! combination. Multiple models may attach to the same child selector.
588//!
589//! ```ignore
590//! pub struct AddOneContext;
591//!
592//! plugin_model! {
593//! name: AddOne,
594//! input: Value,
595//! context: AddOneContext,
596//! bounds: [Value: AtLeast8BitUnsigned],
597//! compute: |v, _ctx| {
598//! v.clone().saturating_add(One::one())
599//! }
600//! }
601//!
602//! define_family! {
603//! root: MathFamilyRoot,
604//! family: OneFamily,
605//! input: Value,
606//! context: AddOneContext
607//! bounds: [Value: AtLeast8BitUnsigned]
608//! child: [
609//! MaybePlusOne => AddOne,
610//! ],
611//! }
612//!```
613//!
614//! ```ignore
615//! pub struct AddNothingContext;
616//!
617//! plugin_model! {
618//! name: AddNothing,
619//! input: mut Value,
620//! context: AddNothingContext,
621//! bounds: [Value: Clone],
622//! compute: |v, _ctx| {
623//! v.clone()
624//! }
625//! }
626//!
627//! define_family! {
628//! root: MathFamilyRoot,
629//! family: NoneFamily,
630//! input: Value,
631//! context: AddNothingContext
632//! bounds: [Value: Clone]
633//! child: [
634//! MaybePlusOne => AddNothing,
635//! ],
636//! }
637//! ```
638//!
639//! Both models attach to the same family and child selector but differ
640//! in unified family type, context and execution behavior.
641//!
642//! ### Pallet Wiring - Remaining Generic
643//!
644//! The pallet implements the caller trait without committing to a concrete
645//! model. It simply forwards the family and context from its configuration.
646//!
647//! ```ignore
648//! struct Pallet<T: Config>(PhantomData<T>);
649//!
650//! impl<T: Config> MathTrait for Pallet<T> {
651//! type Input = T::XInput;
652//! type Output = T::XOutput;
653//! type MathFamily = T::XMathFamily;
654//! type MathContext = T::XMathContext;
655//! }
656//! ```
657//!
658//! This keeps the pallet generic and reusable across runtimes.
659//!
660//! ### Runtime Injection - Selecting the Active Model
661//!
662//! The runtime chooses the concrete behavior by supplying a context that
663//! matches one of the registered models.
664//!
665//! ```ignore
666//! pub trait Config {
667//! type XInput: AtLeast8BitUnsigned;
668//! type XOutput: AtLeast8BitUnsigned;
669//!
670//! plugin_types! {
671//! input: Self::XInput,
672//! output: Self::XOutput,
673//! root: MathFamilyRoot,
674//! family: XMathFamily,
675//! context: XMathContext,
676//! }
677//! }
678//!
679//! plugin_context! {
680//! name: MyContext,
681//! context: AddOneContext,
682//! value: AddOneContext,
683//! }
684//!
685//! pub struct Runtime;
686//!
687//! impl Config for Runtime {
688//! type XInput = u8;
689//! type XOutput = u8;
690//! type XMathFamily = OneFamily;
691//! type XMathContext = AddOneContext;
692//!
693//! // Also can be plugged towards
694//! // type XMathFamily = NoneFamily;
695//! // type XMathContext = AddNothingContext;
696//! }
697//! ```
698//!
699//! ### Resolution Flow
700//!
701//! ```text
702//! Runtime selects:
703//! Family = OneFamily
704//! Child = MaybePlusOne
705//! Context = AddOneContext
706//!
707//! Matching Model:
708//! AddOne<Input=u8, Context=AddOneContext, Output=u8>
709//! ```
710//!
711//! If the runtime instead supplied `NoneFamily` & `AddNothingContext`, the alternative model
712//! would be selected automatically.
713//!
714//! The caller trait never names a concrete model. Instead, the compiler resolves
715//! the correct implementation purely from the type-level contract:
716//!
717//! ```text
718//! (Family, Child, Context) -> Model
719//! ```
720//!
721//! This enables fully static, type-safe plugin resolution without runtime
722//! dispatch or registration tables.
723
724// ===============================================================================
725// ````````````````````````````````` CORE TRAITS `````````````````````````````````
726// ===============================================================================
727
728/// Core trait implemented by all **immutable plugin models**.
729///
730/// A plugin model is typically a zero-sized (stateless) struct that defines
731/// a specific computation strategy. Each model represents a logically distinct
732/// variant within a plugin and may optionally depend on external context
733/// to compute its result.
734///
735/// This trait defines the **pure computation contract**: the input is owned by
736/// caller immutably and must not be mutated. The model returns a new output value
737/// derived from the input and context.
738///
739/// ## Generics
740/// - `Input`: Type of owned-data consumed by the model.
741/// - `Context`: External parameters or configuration required by the model.
742/// - `Output`: Type of value produced by the model.
743///
744/// ## Determinism
745/// Implementations are expected to be stateless and deterministic, producing
746/// the same output for the same input and context.
747pub trait PurePluginModel<Input, Context, Output>: Default {
748 /// Computes the model's output for a given immutable input and context.
749 fn compute(&self, input: Input, context: &Context) -> Output;
750}
751
752/// Trait implemented by **mutable plugin models** that may transform their
753/// input in-place while still producing an output.
754///
755/// Unlike [`PurePluginModel`], this trait explicitly allows mutation of the input,
756/// making it suitable for in-place normalization, sorting, accumulation,
757/// or other performance-sensitive transformations that avoid extra allocations.
758///
759/// Mutation is **explicit and opt-in**, preserving clarity between pure and
760/// state-transforming computations.
761///
762/// ## Generics
763/// - `Mutate`: Type of data that will be mutated in-place.
764/// - `Context`: External parameters or configuration required by the model.
765/// - `Output`: Type of value produced by the model.
766///
767/// ## Semantics
768/// - The input may be modified during computation.
769/// - The returned output may be derived from either the original or mutated state.
770/// - Implementations should still remain stateless with respect to internal storage.
771pub trait MutablePluginModel<Mutate, Context, Output>: Default {
772 /// Computes the model's output while mutating the input in-place.
773 fn compute_mut(&self, input: &mut Mutate, context: &Context) -> Output;
774}
775
776/// Represents a source of context for models.
777///
778/// Models can retrieve context from an implementor of this trait.
779pub trait ModelContext {
780 /// Associated type representing the actual context.
781 type Context;
782
783 /// Returns the context for a model.
784 fn context() -> Self::Context;
785}
786
787/// Placeholder type for models that **do not require any external context**.
788impl ModelContext for () {
789 type Context = ();
790
791 fn context() -> () {
792 ()
793 }
794}
795
796// ===============================================================================
797// ```````````````````````````````` PLUGIN TYPES `````````````````````````````````
798// ===============================================================================
799
800/// Declares **associated plugin types** inside a trait.
801///
802/// Supports both:
803/// - **concrete plugin model binding**, or
804/// - **plugin family binding** for late model selection.
805///
806/// Exactly one of `model` or `family` must be specified.
807/// Exactly one of `input` or `input: mut` must be specified.
808///
809/// ## Syntax
810///
811/// ### Immutable Concrete Model
812///
813/// ```ignore
814/// plugin_types! {
815/// input: InputType, // Required: immutable input type
816/// output: OutputType, // Required: output type
817/// model: ModelAssoc, // Required: associated plugin model
818/// context: ContextAssoc, // Required: associated context provider
819/// }
820/// ```
821///
822/// ### Mutable Concrete Model
823///
824/// ```ignore
825/// plugin_types! {
826/// input: mut MutateType, // Required: mutable input type
827/// output: OutputType, // Required: output type
828/// model: ModelAssoc, // Required: associated plugin model
829/// context: ContextAssoc, // Required: associated context provider
830/// }
831/// ```
832///
833/// ### Plugin Family (Immutable or Mutable)
834///
835/// ```ignore
836/// plugin_types! {
837/// input: InputType, // or: input: mut MutateType
838/// output: OutputType, // Required: output type
839/// borrow: ['a], // Optional: lifetime parameters of input/output
840/// root: PluginFamilyRoot, // Required: plugin family root trait
841/// family: FamilyAssoc, // Required: associated plugin family type
842/// context: ContextAssoc, // Required: associated context provider
843/// provides: [Send + Sync], // Optional: bounds on context
844/// }
845/// ```
846///
847/// ## Lifetimes
848///
849/// - `lifetimes` expands to `<...>` on the **family associated type**
850/// - Enables lifetime-parameterized associated types (GATs)
851///
852/// ## Context Bounds
853///
854/// You may restrict the family's **context type** using `provides`.
855///
856/// ```ignore
857/// plugin_types! {
858/// input: Input,
859/// output: Output,
860/// root: PluginFamilyRoot,
861/// family: FamilyAssoc,
862/// context: MyContext,
863/// provides: [Send + Sync + 'static],
864/// }
865/// ```
866///
867/// Expands roughly to:
868///
869/// ```ignore
870/// type MyContext: ModelContext<Context: Send + Sync + 'static>;
871/// ```
872///
873/// ## Input / Output Constraints
874///
875/// ### Plugin Models (Immutable & Mutable)
876/// `Input` and `Output` must not contain generics or lifetime-based types (no GATs)
877///
878/// ### Plugin Families
879/// - `Input` and `Output` may include **lifetimes**
880/// - Enabled via parameter `borrow: ['a]`
881/// - Type generics are still not supported
882///
883/// ```text
884/// Model -> concrete associated types only (no generics, no lifetimes, no GATS)
885/// Family -> supports lifetimes only (via borrow)
886/// ```
887///
888/// ## Examples
889///
890/// ### Concrete Model
891///
892/// ```ignore
893/// pub trait Increment {
894/// type Input;
895/// type Output;
896///
897/// plugin_types! {
898/// input: Self::Input,
899/// output: Self::Output,
900/// model: AddOneModel,
901/// context: AddOneCtx,
902/// }
903/// }
904/// ```
905///
906/// ### Plugin Family
907///
908/// ```ignore
909/// pub trait MathOps {
910/// type Input;
911/// type Output;
912///
913/// plugin_types! {
914/// input: Self::Input,
915/// output: Self::Output,
916/// root: MathFamilyRoot,
917/// family: MathFamily,
918/// context: MathContext,
919/// }
920/// }
921/// ```
922#[macro_export]
923macro_rules! plugin_types {
924
925 // Immutable model arm
926 (
927 input: $InputTy:ty,
928 output: $OutputTy:ty,
929 $(#[$model_meta:meta])*
930 model: $ModelAssoc:ident,
931 $(#[$ctx_meta:meta])*
932 context: $ContextAssoc:ident $(,)?
933 ) => {
934 $(#[$model_meta])*
935 type $ModelAssoc: $crate::plugins::PurePluginModel<
936 $InputTy,
937 <Self::$ContextAssoc as $crate::plugins::ModelContext>::Context,
938 $OutputTy,
939 > + Default;
940
941 $(#[$ctx_meta])*
942 type $ContextAssoc:
943 $crate::plugins::ModelContext;
944 };
945
946 // Mutable model arm
947 (
948 input: mut $MutateTy:ty,
949 output: $OutputTy:ty,
950 $(#[$model_meta:meta])*
951 model: $ModelAssoc:ident,
952 $(#[$ctx_meta:meta])*
953 context: $ContextAssoc:ident $(,)?
954 ) => {
955 $(#[$model_meta])*
956 type $ModelAssoc: $crate::plugins::MutablePluginModel<
957 $MutateTy,
958 <Self::$ContextAssoc as $crate::plugins::ModelContext>::Context,
959 $OutputTy,
960 > + Default;
961
962 $(#[$ctx_meta])*
963 type $ContextAssoc:
964 $crate::plugins::ModelContext;
965 };
966
967 // Family model arm
968 (
969 input: $(mut)? $InputTy:ty,
970 output: $OutputTy:ty,
971 $(borrow: [$($borrow_lt:lifetime)* $(,)?],)?
972 root: $Root:ident,
973 $(#[$model_meta:meta])*
974 family: $FamilyAssoc:ident,
975 $(#[$ctx_meta:meta])*
976 context: $ContextAssoc:ident
977 $(, provides: [$($provider:tt)*])? $(,)?
978 ) => {
979 $(#[$model_meta])*
980 type $FamilyAssoc $(<$($borrow_lt)*>)? : $Root<
981 $InputTy,
982 <Self::$ContextAssoc as $crate::plugins::ModelContext>::Context,
983 $OutputTy,
984 >;
985
986 $(#[$ctx_meta])*
987 type $ContextAssoc:
988 $crate::plugins::ModelContext$(<Context: $($provider)*>)?;
989 };
990
991}
992
993// ===============================================================================
994// ``````````````````````````````` PLUGIN CONTEXT ````````````````````````````````
995// ===============================================================================
996
997/// Generates a stateless plugin context marker type for a plugin model or a
998/// plugin family.
999///
1000/// This macro defines:
1001/// 1. A zero-sized **marker struct** representing the plugin context provider.
1002/// 2. An implementation of the [`ModelContext`] trait for that marker,
1003/// including a constructor function `context()` returning a value on demand.
1004///
1005/// The `value` construction is on-demand (via [`ModelContext::context`]) which
1006/// allows the context to depend on other constants, statics, or computed values
1007/// while remaining compile-time friendly.
1008///
1009/// When `marker` is specified, the generated struct stores them in `PhantomData`
1010/// fields so the type system correctly tracks them without affecting runtime behavior.
1011///
1012/// - Type generics are tracked using `PhantomData<(T, ...)>`, preserving the
1013/// usual marker semantics for type parameters.
1014///
1015/// ## Syntax
1016///
1017/// ```ignore
1018/// plugin_context! {
1019/// #[attributes...] // Optional: struct-level attributes (docs, derives, etc.)
1020/// name: pub ContextName, // Required: visibility and name of the context marker struct
1021/// context: ContextType, // Required: type representing the context data
1022///
1023/// marker: [T, U], // Optional: phantom-data parameters applied to the marker
1024/// bounds: [T: Default, U: Clone], // Optional: trait bounds for the generated impl
1025///
1026/// value: ContextExpression, // Required: expression producing the context value
1027/// }
1028/// ```
1029///
1030/// ## Attributes
1031/// Optional doc comments or other attributes can be attached to the generated
1032/// marker by placing them above the macro invocation.
1033///
1034/// ## Generics Support
1035///
1036/// - Only **type generics** (`T`, `U`, etc.) are supported via `marker: [...]`
1037/// - Lifetime generics are **not supported**
1038/// - The generics are tracked using `PhantomData` and do not affect runtime behavior
1039///
1040/// ## Examples
1041///
1042/// ### Basic Context
1043///
1044/// ```ignore
1045/// plugin_context! {
1046/// name: pub ElectionContext,
1047/// context: PhragmenConfig,
1048/// value: PhragmenConfig { sequential: true }
1049/// }
1050/// ```
1051///
1052/// ### Generic Context
1053///
1054/// ```ignore
1055/// plugin_context! {
1056/// name: pub GenericContext,
1057/// marker: [T],
1058/// context: PhragmenConfig<T>,
1059/// value: PhragmenConfig { sequential: true }
1060/// }
1061/// ```
1062///
1063#[macro_export]
1064macro_rules! plugin_context {
1065 (
1066 $(#[$meta:meta])*
1067 name: $vis:vis $Name:ident,
1068 context: $ContextType:ty,
1069 $(marker: [$($marker_gen:ident),* $(,)?],)?
1070 $(bounds: [$($bounds:tt)*],)?
1071 value: $ContextLiteral:expr $(,)?
1072 ) => {
1073 $crate::__phantom_struct!(
1074 $(#[$meta])*
1075 #[allow(unused)]
1076 $vis
1077 $Name
1078 []
1079 [$($($marker_gen),*)?]
1080 );
1081
1082 impl $(< $($marker_gen,)* >)?
1083 $crate::plugins::ModelContext
1084 for $Name $(< $($marker_gen,)* >)?
1085 $(where $($bounds)*)?
1086 {
1087 type Context = $ContextType;
1088
1089 fn context() -> Self::Context {
1090 $ContextLiteral
1091 }
1092 }
1093 };
1094}
1095
1096// ===============================================================================
1097// ```````````````````````````````` PLUGIN OUTPUT ````````````````````````````````
1098// ===============================================================================
1099
1100/// Generates a strongly-typed associated function that executes a plugin model
1101/// and returns its computed output.
1102///
1103/// The macro expands to a function which:
1104/// - Instantiates the plugin model using `Default`
1105/// - Constructs the execution context via [`ModelContext::context`]
1106/// - Wraps the input, model, and context into the appropriate execution source
1107/// - Executes the model and returns the resulting output
1108///
1109/// This removes boilerplate wiring of model construction, context resolution,
1110/// and execution, while preserving full compile-time type safety.
1111///
1112/// Exactly one of the following must be specified:
1113/// - `model` -> directly executes a concrete plugin model
1114/// - `root` + `family` + `child` -> resolves a model from a plugin family
1115///
1116/// Exactly one of:
1117/// - `input:` -> immutable execution contract ([`PurePluginModel`])
1118/// - `input: mut` -> mutable execution contract ([`MutablePluginModel`])
1119///
1120/// Optional:
1121/// - `borrow` declares function-level generic parameters for input and output
1122/// specialization in family models.
1123///
1124/// ## Syntax
1125///
1126/// ### Immutable Concrete Model
1127///
1128/// ```ignore
1129/// plugin_output! {
1130/// pub fn run_model, // Required: function visibility and name to generate
1131/// input: MyInput, // Required: immutable input type
1132/// output: MyOutput, // Required: output type produced by the model
1133/// model: MyModel, // Required: concrete immutable plugin model type
1134/// context: MyContext, // Required: context provider implementing ModelContext
1135/// }
1136/// ```
1137///
1138/// Expands to:
1139/// `pub fn run_model(input: MyInput) -> MyOutput { ... }`
1140///
1141/// ### Mutable Concrete Model
1142///
1143/// ```ignore
1144/// plugin_output! {
1145/// pub fn run_model_mut, // Required: function visibility and name to generate
1146/// input: mut MyInput, // Required: mutable input type
1147/// output: MyOutput, // Required: output type produced by the model
1148/// model: MyModel, // Required: concrete mutable plugin model type
1149/// context: MyContext, // Required: context provider implementing ModelContext
1150/// }
1151/// ```
1152///
1153/// ### Immutable Family-Selected Model
1154///
1155/// ```ignore
1156/// plugin_output! {
1157/// pub fn run_family, // Required: function visibility and name to generate
1158/// input: MyInput<'a>, // Required: immutable input type
1159/// output: MyOutput, // Required: output type produced by the model
1160/// borrow: ['a], // Optional: function-level liftimes over input/output
1161/// root: MyFamilyRoot, // Required: plugin family root trait
1162/// family: MyFamily, // Required: concrete plugin family type
1163/// child: MyChildMarker, // Required: child model identifier within the family
1164/// context: MyContext, // Required: context provider implementing ModelContext
1165/// }
1166/// ```
1167///
1168/// ### Mutable Family-Selected Model
1169///
1170/// ```ignore
1171/// plugin_output! {
1172/// pub fn run_family_mut, // Required: function visibility and name to generate
1173/// input: mut MyInput, // Required: mutable input type
1174/// output: MyOutput<'a>, // Required: output type produced by the model
1175/// borrow: ['a], // Optional: function-level liftimes over input/output
1176/// root: MyFamilyRoot, // Required: plugin family root trait
1177/// family: MyFamily, // Required: concrete plugin family type
1178/// child: MyChildMarker, // Required: child model identifier within the family
1179/// context: MyContext, // Required: context provider implementing ModelContext
1180/// }
1181/// ```
1182///
1183/// ## Semantics
1184///
1185/// - `model` form executes a fixed concrete plugin model.
1186/// - `root` + `family` + `child` form defers model selection to the plugin family,
1187/// where the concrete model is resolved at compile time using the
1188/// `(Input, Context, Output, Family)` signature.
1189/// - `Context` acts as the nominal discriminator within a family, while
1190/// `Input` and `Output` are validated once concretely resolved at the call site.
1191/// - Mutable variants may mutate the input in-place, but resolution remains
1192/// entirely static through trait bounds.
1193/// - All resolution is performed at compile time; no dynamic dispatch is used.
1194///
1195/// ## Input / Output Constraints
1196///
1197/// - Plugin models (immutable & mutable) use **non-GAT types only**
1198/// - No generics
1199/// - No lifetime-based types (no GATs)
1200///
1201/// - Plugin families may use **lifetimes only**
1202/// - Enabled via `borrow: ['a]`
1203/// - Type generics are not supported
1204///
1205/// ```text
1206/// Model -> concrete types only
1207/// Family -> supports lifetimes only
1208/// ```
1209#[macro_export]
1210macro_rules! plugin_output {
1211 // Immutable model function
1212 (
1213 $(#[$meta:meta])*
1214 $vis:vis fn $name:ident,
1215 input: $Input:ty,
1216 output: $Output:ty,
1217 model: $ModelType:ty,
1218 context: $ContextType:ty $(,)?
1219 ) => {
1220 $(#[$meta])*
1221 $vis fn $name (input: $Input) -> $Output
1222 {
1223 // Instantiate the model
1224 let model = <$ModelType>::default();
1225
1226 // Construct the context
1227 let context: <$ContextType as $crate::plugins::ModelContext>::Context =
1228 <$ContextType as $crate::plugins::ModelContext>::context();
1229
1230 // Compute and return output
1231 $crate::plugins::PurePluginModel::<_, _, _>::compute(&model, input, &context)
1232 }
1233 };
1234
1235 // Mutable model function
1236 (
1237 $(#[$meta:meta])*
1238 $vis:vis fn $name:ident,
1239 input: mut $Input:ty,
1240 output: $Output:ty,
1241 model: $ModelType:ty,
1242 context: $ContextType:ty $(,)?
1243 ) => {
1244 $(#[$meta])*
1245 $vis fn $name(input: &mut $Input) -> $Output
1246 {
1247 // Instantiate the model
1248 let model = <$ModelType>::default();
1249
1250 // Construct the context
1251 let context: <$ContextType as $crate::plugins::ModelContext>::Context =
1252 <$ContextType as $crate::plugins::ModelContext>::context();
1253
1254 // Compute and return output
1255 $crate::plugins::MutablePluginModel::<_, _, _>::compute_mut(&model, input, &context)
1256 }
1257 };
1258
1259 // Immutable Family Child-specific function
1260 (
1261 $(#[$meta:meta])*
1262 $vis:vis fn $name:ident,
1263 input: $Input:ty,
1264 output: $Output:ty,
1265 $(borrow: [$($borrow_lt:lifetime)* $(,)?],)?
1266 root: $Root:ident,
1267 family: $Family:ty,
1268 child: $Child:ident,
1269 context: $ContextType:ty $(,)?
1270 ) => {
1271 #[doc = concat!(
1272 "Plugin invocation pure-function for the child - [`",
1273 stringify!($Child),
1274 "`] of the plugin family - [`",
1275 stringify!($Root),
1276 "`]"
1277 )]
1278 $(#[$meta])*
1279 $vis fn $name $(<$($borrow_lt)*>)? (input: $Input) -> $Output
1280 {
1281 // Resolve the concrete plugin model from the family using the root trait
1282 let model =
1283 <$Family as $Root<$Input,<$ContextType as $crate::plugins::ModelContext>::Context,
1284 $Output>>::$Child::default();
1285
1286 // Construct the execution context via the context provider.
1287 let context =
1288 <$ContextType as $crate::plugins::ModelContext>::context();
1289
1290 // Execute the immutable plugin model and return the computed output.
1291 <<$Family as $Root<$Input,<$ContextType as $crate::plugins::ModelContext>::Context,
1292 $Output>>::$Child
1293 as $crate::plugins::PurePluginModel<
1294 $Input,
1295 <$ContextType as $crate::plugins::ModelContext>::Context,
1296 $Output,
1297 >>::compute(&model, input, &context)
1298 }
1299 };
1300
1301 // Mutable Family Child-specific function
1302 (
1303 $(#[$meta:meta])*
1304 $vis:vis fn $name:ident,
1305 input: mut $Input:ty,
1306 output: $Output:ty,
1307 $(borrow: [$($borrow_lt:lifetime)* $(,)?],)?
1308 root: $Root:ident,
1309 family: $Family:ty,
1310 child: $Child:ident,
1311 context: $ContextType:ty $(,)?
1312 ) => {
1313 #[doc = concat!(
1314 "Plugin invocation mutable-function for the child - [`",
1315 stringify!($Child),
1316 "`] of the plugin family - [`",
1317 stringify!($Root),
1318 "`]"
1319 )]
1320 $(#[$meta])*
1321 $vis fn $name $(<$($borrow_lt)*>)? (input: &mut $Input) -> $Output
1322 {
1323 // Resolve the concrete plugin model from the family using the root trait
1324 let model =
1325 <$Family as $Root<$Input,<$ContextType as $crate::plugins::ModelContext>::Context,
1326 $Output>>::$Child::default();
1327
1328 // Construct the execution context via the context provider.
1329 let context =
1330 <$ContextType as $crate::plugins::ModelContext>::context();
1331
1332 // Execute the immutable plugin model and return the computed output.
1333 <<$Family as $Root<$Input,<$ContextType as $crate::plugins::ModelContext>::Context,
1334 $Output>>::$Child
1335 as $crate::plugins::MutablePluginModel<
1336 $Input,
1337 <$ContextType as $crate::plugins::ModelContext>::Context,
1338 $Output,
1339 >>::compute_mut(&model, input, &context)
1340 }
1341 };
1342}
1343
1344// ===============================================================================
1345// ```````````````````````````````` PLUGIN TESTS `````````````````````````````````
1346// ===============================================================================
1347
1348/// Generates **table-driven unit tests** for plugin models.
1349///
1350/// It supports both **immutable** and **mutable** models, with or without context,
1351/// and with either explicit or inferred output types.
1352///
1353/// For each test case, the macro:
1354/// - Instantiates the plugin model using `Default`
1355/// - Constructs the required context (if any)
1356/// - Executes the model's computation (`compute` for immutable models,
1357/// `compute_mut` for mutable models)
1358/// - Asserts that the computed output matches the expected value
1359/// - Optionally asserts the final mutated input state for mutable models
1360///
1361/// Each test case expands into an **independent `#[test]` function**, ensuring
1362/// clear isolation and accurate failure reporting.
1363///
1364/// ## Features
1365///
1366/// - Supports **immutable** (`PurePluginModel`) and **mutable** (`MutablePluginModel`) models
1367/// - Supports **context-aware** and **context-free** plugin models
1368/// - Supports **explicit output types** or **implicit output = input**
1369/// - Optional assertion of the **mutated input value** for mutable models
1370/// - Generates one `#[test]` function per case
1371/// - Avoids boilerplate while preserving full type safety
1372/// - Mirrors the exact runtime execution contract of plugin models
1373///
1374/// ## Supported Forms
1375///
1376/// The macro supports the same four combinations for both immutable and mutable models:
1377///
1378/// | Context | Output |
1379/// +-------+------+
1380/// | Yes | Explicit |
1381/// | Yes | Inferred (output = input) |
1382/// | No | Explicit |
1383/// | No | Inferred (output = input) |
1384///
1385/// Mutable models are declared by using `input: mut Type`, which indicates that the
1386/// model will receive `&mut Type` and may transform the input in-place.
1387///
1388/// ## Syntax
1389///
1390/// ```ignore
1391/// plugin_test! {
1392/// model: ModelType, // Plugin model type to test
1393/// input: InputType | mut InputType, // `mut` enables mutable model testing
1394/// output: OutputType, // Optional: defaults to `InputType` if omitted
1395/// context: ContextType, // Optional: required if model uses context
1396/// value: context_expr, // Optional: expression constructing the context
1397/// cases: {
1398/// (test_name, input_expr, expected_output),
1399/// (test_name_2, input_expr_2, expected_output_2, expected_mutated_input), // mutable only
1400/// }
1401/// }
1402/// ```
1403///
1404/// - `model`: Plugin model type implementing `PurePluginModel` or `MutablePluginModel`
1405/// - `input`: Input type consumed by the model (`mut` indicates in-place mutation)
1406/// - `output`: Output type produced by the model (defaults to input type if omitted)
1407/// - `context`: Context type required by the model (omit for `()`)
1408/// - `value`: Expression that constructs the context instance
1409/// - `cases`: List of test tuples
1410///
1411/// Each case tuple has the form:
1412/// - `(name, input, expected_output)` for immutable models
1413/// - `(name, input, expected_output)` for mutable models when only output is asserted
1414/// - `(name, input, expected_output, expected_mutated_input)` to also verify
1415/// the final mutated state of the input
1416///
1417/// When the fourth element is provided, the macro additionally checks that the
1418/// input was correctly transformed in-place.
1419///
1420/// ## Notes
1421///
1422/// - Each test case expands into a **separate `#[test]` function**
1423/// - Context and input types must match the model's trait implementation
1424/// - Output inference (`output = input`) follows the same rule as `plugin_model!`
1425/// - Compilation fails if input, context, or output types are incompatible
1426///
1427/// This macro is intended for **testing plugin model logic in isolation** and
1428/// should not be used for testing pallet storage, dispatchables, or runtime configuration.
1429#[macro_export]
1430macro_rules! plugin_test {
1431 // Helper: choose output type, defaulting to input if not provided
1432 (@output_ty $InputType:ty) => { $InputType };
1433 (@output_ty $InputType:ty, $OutputType:ty) => { $OutputType };
1434
1435 // WITH CONTEXT, EXPLICIT OUTPUT
1436 (
1437 model: $ModelName:ty,
1438 input: $InputTy:ty,
1439 output: $OutputTy:ty,
1440 context: $ContextTy:ty,
1441 value: $ContextExpr:expr,
1442 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr)),* $(,)? }
1443 ) => {
1444 $(
1445 #[test] // generate a #[test] function for each case
1446 fn $test_name() {
1447 let model = <$ModelName>::default(); // instantiate model
1448 let context: $ContextTy = $ContextExpr; // construct context
1449 let input: $InputTy = $input_expr; // test input
1450 let result: $OutputTy = // compute output
1451 <$ModelName as $crate::plugins::PurePluginModel<
1452 $InputTy,
1453 $ContextTy,
1454 $OutputTy
1455 >>::compute(&model, input, &context);
1456 assert_eq!(result, $expected); // verify result
1457 }
1458 )*
1459 };
1460
1461 // WITH CONTEXT, OUTPUT = INPUT
1462 (
1463 model: $ModelName:ty,
1464 input: $InputTy:ty,
1465 context: $ContextTy:ty,
1466 value: $ContextExpr:expr,
1467 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr)),* $(,)? }
1468 ) => {
1469 $(
1470 #[test]
1471 fn $test_name() {
1472 type Output = $InputTy;
1473 let model = <$ModelName>::default();
1474 let context: $ContextTy = $ContextExpr;
1475 let input: $InputTy = $input_expr;
1476 let result: Output =
1477 <$ModelName as $crate::plugins::PurePluginModel<
1478 $InputTy,
1479 $ContextTy,
1480 Output
1481 >>::compute(&model, input, &context);
1482 assert_eq!(result, $expected);
1483 }
1484 )*
1485 };
1486
1487 // No Context, EXPLICIT OUTPUT
1488 (
1489 model: $ModelName:ty,
1490 input: $InputTy:ty,
1491 output: $OutputTy:ty,
1492 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr)),* $(,)? }
1493 ) => {
1494 $(
1495 #[test]
1496 fn $test_name() {
1497 let model = <$ModelName>::default();
1498 let context: () = Default::default();
1499 let input: $InputTy = $input_expr;
1500 let result: $OutputTy =
1501 <$ModelName as $crate::plugins::PurePluginModel<
1502 $InputTy,
1503 (),
1504 $OutputTy
1505 >>::compute(&model, input, &context);
1506 assert_eq!(result, $expected);
1507 }
1508 )*
1509 };
1510
1511 // No Context, OUTPUT = INPUT
1512 (
1513 model: $ModelName:ty,
1514 input: $InputTy:ty,
1515 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr)),* $(,)? }
1516 ) => {
1517 $(
1518 #[test]
1519 fn $test_name() {
1520 type Output = $InputTy;
1521 let model = <$ModelName>::default();
1522 let context: () = Default::default();
1523 let input: $InputTy = $input_expr;
1524 let result: Output =
1525 <$ModelName as $crate::plugins::PurePluginModel<
1526 $InputTy,
1527 (),
1528 Output
1529 >>::compute(&model, input, &context);
1530 assert_eq!(result, $expected);
1531 }
1532 )*
1533 };
1534
1535 // WITH CONTEXT, EXPLICIT OUTPUT (MUTABLE)
1536 (
1537 model: $ModelName:ty,
1538 input: mut $InputTy:ty,
1539 output: $OutputTy:ty,
1540 context: $ContextTy:ty,
1541 value: $ContextExpr:expr,
1542 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr $(, $expected_input:expr)?)),* $(,)? }
1543 ) => {
1544 $(
1545 #[test]
1546 fn $test_name() {
1547 let model = <$ModelName>::default();
1548 let context: $ContextTy = $ContextExpr;
1549 let mut input: $InputTy = $input_expr;
1550
1551 let result: $OutputTy =
1552 <$ModelName as $crate::plugins::MutablePluginModel<
1553 $InputTy,
1554 $ContextTy,
1555 $OutputTy
1556 >>::compute_mut(&model, &mut input, &context);
1557
1558 assert_eq!(result, $expected);
1559
1560 $(
1561 assert_eq!(input, $expected_input);
1562 )?
1563 }
1564 )*
1565 };
1566
1567 // WITH CONTEXT, OUTPUT = INPUT (MUTABLE)
1568 (
1569 model: $ModelName:ty,
1570 input: mut $InputTy:ty,
1571 context: $ContextTy:ty,
1572 value: $ContextExpr:expr,
1573 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr $(, $expected_input:expr)?)),* $(,)? }
1574 ) => {
1575 $(
1576 #[test]
1577 fn $test_name() {
1578 type Output = $InputTy;
1579 let model = <$ModelName>::default();
1580 let context: $ContextTy = $ContextExpr;
1581 let mut input: $InputTy = $input_expr;
1582
1583 let result: Output =
1584 <$ModelName as $crate::plugins::MutablePluginModel<
1585 $InputTy,
1586 $ContextTy,
1587 Output
1588 >>::compute_mut(&model, &mut input, &context);
1589
1590 assert_eq!(result, $expected);
1591
1592 $(
1593 assert_eq!(input, $expected_input);
1594 )?
1595 }
1596 )*
1597 };
1598
1599 // No Context, EXPLICIT OUTPUT (MUTABLE)
1600 (
1601 model: $ModelName:ty,
1602 input: mut $InputTy:ty,
1603 output: $OutputTy:ty,
1604 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr $(, $expected_input:expr)?)),* $(,)? }
1605 ) => {
1606 $(
1607 #[test]
1608 fn $test_name() {
1609 let model = <$ModelName>::default();
1610 let context: () = Default::default();
1611 let mut input: $InputTy = $input_expr;
1612
1613 let result: $OutputTy =
1614 <$ModelName as $crate::plugins::MutablePluginModel<
1615 $InputTy,
1616 (),
1617 $OutputTy
1618 >>::compute_mut(&model, &mut input, &context);
1619
1620 assert_eq!(result, $expected);
1621
1622 $(
1623 assert_eq!(input, $expected_input);
1624 )?
1625 }
1626 )*
1627 };
1628
1629 // No Context, OUTPUT = INPUT (MUTABLE)
1630 (
1631 model: $ModelName:ty,
1632 input: mut $InputTy:ty,
1633 cases: { $(($test_name:ident, $input_expr:expr, $expected:expr $(, $expected_input:expr)?)),* $(,)? }
1634 ) => {
1635 $(
1636 #[test]
1637 fn $test_name() {
1638 type Output = $InputTy;
1639 let model = <$ModelName>::default();
1640 let context: () = Default::default();
1641 let mut input: $InputTy = $input_expr;
1642
1643 let result: Output =
1644 <$ModelName as $crate::plugins::MutablePluginModel<
1645 $InputTy,
1646 (),
1647 Output
1648 >>::compute_mut(&model, &mut input, &context);
1649
1650 assert_eq!(result, $expected);
1651
1652 $(
1653 assert_eq!(input, $expected_input);
1654 )?
1655 }
1656 )*
1657 };
1658}
1659
1660// ===============================================================================
1661// ```````````````````````````````` PLUGIN MODEL `````````````````````````````````
1662// ===============================================================================
1663
1664/// Defines a plugin model in a fully generic, and type-safe way.
1665///
1666/// The macro generates:
1667/// - A `struct` representing the plugin model (deriving `Default`)
1668/// - An implementation of either [`PurePluginModel`] or [`MutablePluginModel`]
1669///
1670/// This removes repetitive boilerplate while ensuring that the relationships
1671/// between input, output, and context types are enforced at compile time.
1672///
1673/// Exactly one of:
1674/// - `input:` -> immutable model (`PurePluginModel`)
1675/// - `input: mut` -> mutable model (`MutablePluginModel`)
1676///
1677/// Optionally:
1678/// - `context:` enables contextual execution (otherwise context defaults to `()`)
1679/// - `root:` + `child:` attaches the model to a plugin family for late resolution
1680///
1681/// ## Syntax
1682///
1683/// ### Immutable Model (No Context)
1684///
1685/// ```ignore
1686/// plugin_model! {
1687/// name: pub ModelName, // Required: struct visibility and struct name of the plugin model
1688/// input: InputType, // Required: generic immutable input type
1689/// output: OutputType, // Optional: output type (defaults to input if omitted)
1690/// others: [T1, T2], // Optional: additional generic parameters
1691/// bounds: [TraitBounds], // Required: trait bounds for generics
1692/// compute: |input, ctx| { ... } // Required: compute logic (`ctx` is `()`)
1693/// }
1694/// ```
1695///
1696/// ### Immutable Model with Context
1697///
1698/// ```ignore
1699/// plugin_model! {
1700/// name: pub ModelName, // Required: struct visibility and struct name of the plugin model
1701/// input: InputType, // Required: generic immutable input type
1702/// output: OutputType, // Optional: output type (defaults to input if omitted)
1703/// others: [T1, T2], // Optional: additional generic parameters
1704/// context: ContextType, // Required: context struct used during execution
1705/// bounds: [TraitBounds], // Required: trait bounds for generics
1706/// compute: |input, ctx| { ... } // `ctx: &ContextType`
1707/// }
1708/// ```
1709///
1710/// ### Mutable Model
1711///
1712/// ```ignore
1713/// plugin_model! {
1714/// name: pub ModelName, // Required: struct visibility and struct name of the plugin model
1715/// input: mut InputType, // Required: mutable input type (`&mut InputType`)
1716/// output: OutputType, // Optional: output type (defaults to immutable input type)
1717/// others: [T1, T2], // Optional: additional generic parameters
1718/// context: ContextType, // Optional: context struct (defaults to `()`)
1719/// bounds: [TraitBounds], // Required: trait bounds for generics
1720/// compute: |input, ctx| { ... } // Uses `compute_mut`
1721/// }
1722/// ```
1723///
1724/// ## Output Type Semantics
1725///
1726/// - If `output` is omitted, the output type defaults to the **immutable input type**,
1727/// even for mutable models (it does **not** default to `()`).
1728/// - This rule applies to both immutable and mutable plugin models.
1729/// - If a unit output `()` is desired, it must be specified explicitly as:
1730///
1731/// ```ignore
1732/// output: Output,
1733/// bounds: [Output: Default]
1734/// ```
1735///
1736/// The `Default` bound is required so `compute`'s block can construct the output value.
1737///
1738/// ## Semantics
1739///
1740/// - Each model is fully generic over its input, output, and optional context.
1741/// - If `context` is omitted, the model uses `()` as its context type.
1742/// - If `root` and `child` are provided, the model becomes a member of a plugin
1743/// family and is selected indirectly using the `(root, child, context)`
1744/// resolution lattice.
1745/// - Immutable variants use `compute` with shared input references.
1746/// - Mutable variants use `compute_mut` and may mutate the input in-place.
1747///
1748/// All constraints are enforced purely through trait bounds and associated
1749/// types, guaranteeing compile-time correctness of model wiring and resolution.
1750#[macro_export]
1751macro_rules! plugin_model {
1752
1753 // Helper Rule: `@output_ty`
1754 (@output_ty $Input:tt) => { $Input };
1755 (@output_ty $Input:tt, $Output:tt) => { $Output };
1756
1757 // Variant 1: No Context, Single Input, Single Output
1758 (
1759 $(#[$name_meta:meta])*
1760 name: $vis:vis $ModelName:ident,
1761 input: $Input:ident,
1762 $(output: $Output:ident ,)?
1763 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1764 bounds: [$($bounds:tt)*],
1765 $(#[$compute_meta:meta])*
1766 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1767 ) => {
1768 #[derive(Debug, Default)]
1769 $(#[$name_meta])*
1770 $vis struct $ModelName;
1771
1772 impl<
1773 $($($other_gen ,)*)?
1774 $Input
1775 $(, $Output)?
1776 > $crate::plugins::PurePluginModel<
1777 $Input,
1778 (),
1779 $crate::plugin_model!(@output_ty $Input $(, $Output)?)
1780 > for $ModelName
1781 where
1782 $($bounds)*
1783 {
1784 $(#[$compute_meta])*
1785 fn compute(
1786 &self,
1787 $input_arg: $Input,
1788 $ctx_arg: &()
1789 ) -> $crate::plugin_model!(@output_ty $Input $(, $Output)?) {
1790 $body
1791 }
1792 }
1793 };
1794
1795 // Variant 2: No Context, Single Input, Tuple Output
1796 (
1797 $(#[$name_meta:meta])*
1798 name: $vis:vis $ModelName:ident,
1799 input: $Input:ident,
1800 $(output: ($($Output:ident),+) ,)?
1801 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1802 bounds: [$($bounds:tt)*],
1803 $(#[$compute_meta:meta])*
1804 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1805 ) => {
1806 #[derive(Debug, Default)]
1807 $(#[$name_meta])*
1808 $vis struct $ModelName;
1809
1810 impl<
1811 $($($other_gen ,)*)?
1812 $Input
1813 $(, $($Output),+)?
1814 > $crate::plugins::PurePluginModel<
1815 $Input,
1816 (),
1817 $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?)
1818 > for $ModelName
1819 where
1820 $($bounds)*
1821 {
1822 $(#[$compute_meta])*
1823 fn compute(
1824 &self,
1825 $input_arg: $Input,
1826 $ctx_arg: &()
1827 ) -> $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?) {
1828 $body
1829 }
1830 }
1831 };
1832
1833 // Variant 3: No Context, Tuple Input, Single Output
1834 (
1835 $(#[$name_meta:meta])*
1836 name: $vis:vis $ModelName:ident,
1837 input: ($($Input:ident),+),
1838 $(output: $Output:ident ,)?
1839 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1840 bounds: [$($bounds:tt)*],
1841 $(#[$compute_meta:meta])*
1842 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1843 ) => {
1844 #[derive(Debug, Default)]
1845 $(#[$name_meta])*
1846 $vis struct $ModelName;
1847
1848 impl<
1849 $($($other_gen ,)*)?
1850 $($Input),+
1851 $(, $Output)?
1852 > $crate::plugins::PurePluginModel<
1853 ($($Input),+),
1854 (),
1855 $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?)
1856 > for $ModelName
1857 where
1858 $($bounds)*
1859 {
1860 $(#[$compute_meta])*
1861 fn compute(
1862 &self,
1863 $input_arg: ($($Input),+),
1864 $ctx_arg: &()
1865 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?) {
1866 $body
1867 }
1868 }
1869 };
1870
1871 // Variant 4: No Context, Tuple Input, Tuple Output
1872 (
1873 $(#[$name_meta:meta])*
1874 name: $vis:vis $ModelName:ident,
1875 input: ($($Input:ident),+),
1876 $(output: ($($Output:ident),+) ,)?
1877 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1878 bounds: [$($bounds:tt)*],
1879 $(#[$compute_meta:meta])*
1880 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1881 ) => {
1882 #[derive(Debug, Default)]
1883 $(#[$name_meta])*
1884 $vis struct $ModelName;
1885
1886 impl<
1887 $($($other_gen ,)*)?
1888 $($Input),+
1889 $(, $($Output),+)?
1890 > $crate::plugins::PurePluginModel<
1891 ($($Input),+),
1892 (),
1893 $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?)
1894 > for $ModelName
1895 where
1896 $($bounds)*
1897 {
1898 $(#[$compute_meta])*
1899 fn compute(
1900 &self,
1901 $input_arg: ($($Input),+),
1902 $ctx_arg: &()
1903 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?) {
1904 $body
1905 }
1906 }
1907 };
1908
1909 // Variant 5: Context, Single Input, Single Output
1910 (
1911 $(#[$name_meta:meta])*
1912 name: $vis:vis $ModelName:ident,
1913 input: $Input:ident,
1914 $(output: $Output:ident ,)?
1915 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1916 context: $Context:ty,
1917 bounds: [$($bounds:tt)*],
1918 $(#[$compute_meta:meta])*
1919 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1920 ) => {
1921 #[derive(Debug, Default)]
1922 $(#[$name_meta])*
1923 $vis struct $ModelName;
1924
1925 impl<
1926 $($($other_gen ,)*)?
1927 $Input
1928 $(, $Output)?
1929 > $crate::plugins::PurePluginModel<
1930 $Input,
1931 $Context,
1932 $crate::plugin_model!(@output_ty $Input $(, $Output)?)
1933 > for $ModelName
1934 where
1935 $($bounds)*
1936 {
1937 $(#[$compute_meta])*
1938 fn compute(
1939 &self,
1940 $input_arg: $Input,
1941 $ctx_arg: &$Context
1942 ) -> $crate::plugin_model!(@output_ty $Input $(, $Output)?) {
1943 $body
1944 }
1945 }
1946 };
1947
1948 // Variant 6: Context, Single Input, Tuple Output
1949 (
1950 $(#[$name_meta:meta])*
1951 name: $vis:vis $ModelName:ident,
1952 input: $Input:ident,
1953 $(output: ($($Output:ident),+) ,)?
1954 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1955 context: $Context:ty,
1956 bounds: [$($bounds:tt)*],
1957 $(#[$compute_meta:meta])*
1958 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1959 ) => {
1960 #[derive(Debug, Default)]
1961 $(#[$name_meta])*
1962 $vis struct $ModelName;
1963
1964 impl<
1965 $($($other_gen ,)*)?
1966 $Input
1967 $(, $($Output),+)?
1968 > $crate::plugins::PurePluginModel<
1969 $Input,
1970 $Context,
1971 $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?)
1972 > for $ModelName
1973 where
1974 $($bounds)*
1975 {
1976 $(#[$compute_meta])*
1977 fn compute(
1978 &self,
1979 $input_arg: $Input,
1980 $ctx_arg: &$Context
1981 ) -> $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?) {
1982 $body
1983 }
1984 }
1985 };
1986
1987 // Variant 7: Context, Tuple Input, Single Output
1988 (
1989 $(#[$name_meta:meta])*
1990 name: $vis:vis $ModelName:ident,
1991 input: ($($Input:ident),+),
1992 $(output: $Output:ident ,)?
1993 $(others: [$($other_gen:tt),* $(,)? ] ,)?
1994 context: $Context:ty,
1995 bounds: [$($bounds:tt)*],
1996 $(#[$compute_meta:meta])*
1997 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
1998 ) => {
1999 #[derive(Debug, Default)]
2000 $(#[$name_meta])*
2001 $vis struct $ModelName;
2002
2003 impl<
2004 $($($other_gen ,)*)?
2005 $($Input),+
2006 $(, $Output)?
2007 > $crate::plugins::PurePluginModel<
2008 ($($Input),+),
2009 $Context,
2010 $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?)
2011 > for $ModelName
2012 where
2013 $($bounds)*
2014 {
2015 $(#[$compute_meta])*
2016 fn compute(
2017 &self,
2018 $input_arg: ($($Input),+),
2019 $ctx_arg: &$Context
2020 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?) {
2021 $body
2022 }
2023 }
2024 };
2025
2026 // Variant 8: Context, Tuple Input, Tuple Output
2027 (
2028 $(#[$name_meta:meta])*
2029 name: $vis:vis $ModelName:ident,
2030 input: ($($Input:ident),+),
2031 $(output: ($($Output:ident),+) ,)?
2032 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2033 context: $Context:ty,
2034 bounds: [$($bounds:tt)*],
2035 $(#[$compute_meta:meta])*
2036 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2037 ) => {
2038 #[derive(Debug, Default)]
2039 $(#[$name_meta])*
2040 $vis struct $ModelName;
2041
2042 impl<
2043 $($($other_gen ,)*)?
2044 $($Input),+
2045 $(, $($Output),+)?
2046 > $crate::plugins::PurePluginModel<
2047 ($($Input),+),
2048 $Context,
2049 $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?)
2050 > for $ModelName
2051 where
2052 $($bounds)*
2053 {
2054 $(#[$compute_meta])*
2055 fn compute(
2056 &self,
2057 $input_arg: ($($Input),+),
2058 $ctx_arg: &$Context
2059 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?) {
2060 $body
2061 }
2062 }
2063 };
2064
2065 // Variant 9: No Context, Single Input, Single Output (MUTABLE)
2066 (
2067 $(#[$name_meta:meta])*
2068 name: $vis:vis $ModelName:ident,
2069 input: mut $Input:ident,
2070 $(output: $Output:ident ,)?
2071 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2072 bounds: [$($bounds:tt)*],
2073 $(#[$compute_meta:meta])*
2074 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2075 ) => {
2076 #[derive(Debug, Default)]
2077 $(#[$name_meta])*
2078 $vis struct $ModelName;
2079
2080 impl<
2081 $($($other_gen ,)*)?
2082 $Input
2083 $(, $Output)?
2084 > $crate::plugins::MutablePluginModel<
2085 $Input,
2086 (),
2087 $crate::plugin_model!(@output_ty $Input $(, $Output)?)
2088 > for $ModelName
2089 where
2090 $($bounds)*
2091 {
2092 $(#[$compute_meta])*
2093 fn compute_mut(
2094 &self,
2095 $input_arg: &mut $Input,
2096 $ctx_arg: &()
2097 ) -> $crate::plugin_model!(@output_ty $Input $(, $Output)?) {
2098 $body
2099 }
2100 }
2101 };
2102
2103 // Variant 10: No Context, Single Input, Tuple Output (MUTABLE)
2104 (
2105 $(#[$name_meta:meta])*
2106 name: $vis:vis $ModelName:ident,
2107 input: mut $Input:ident,
2108 $(output: ($($Output:ident),+) ,)?
2109 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2110 bounds: [$($bounds:tt)*],
2111 $(#[$compute_meta:meta])*
2112 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2113 ) => {
2114 #[derive(Debug, Default)]
2115 $(#[$name_meta])*
2116 $vis struct $ModelName;
2117
2118 impl<
2119 $($($other_gen ,)*)?
2120 $Input
2121 $(, $($Output),+)?
2122 > $crate::plugins::MutablePluginModel<
2123 $Input,
2124 (),
2125 $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?)
2126 > for $ModelName
2127 where
2128 $($bounds)*
2129 {
2130 $(#[$compute_meta])*
2131 fn compute_mut(
2132 &self,
2133 $input_arg: &mut $Input,
2134 $ctx_arg: &()
2135 ) -> $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?) {
2136 $body
2137 }
2138 }
2139 };
2140
2141 // Variant 11: No Context, Tuple Input, Single Output (MUTABLE)
2142 (
2143 $(#[$name_meta:meta])*
2144 name: $vis:vis $ModelName:ident,
2145 input: mut ($($Input:ident),+),
2146 $(output: $Output:ident ,)?
2147 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2148 bounds: [$($bounds:tt)*],
2149 $(#[$compute_meta:meta])*
2150 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2151 ) => {
2152 #[derive(Debug, Default)]
2153 $(#[$name_meta])*
2154 $vis struct $ModelName;
2155
2156 impl<
2157 $($($other_gen ,)*)?
2158 $($Input),+
2159 $(, $Output)?
2160 > $crate::plugins::MutablePluginModel<
2161 ($($Input),+),
2162 (),
2163 $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?)
2164 > for $ModelName
2165 where
2166 $($bounds)*
2167 {
2168 $(#[$compute_meta])*
2169 fn compute_mut(
2170 &self,
2171 $input_arg: &mut ($($Input),+),
2172 $ctx_arg: &(),
2173 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?) {
2174 $body
2175 }
2176 }
2177 };
2178
2179 // Variant 12: No Context, Tuple Input, Tuple Output (MUTABLE)
2180 (
2181 $(#[$name_meta:meta])*
2182 name: $vis:vis $ModelName:ident,
2183 input: mut ($($Input:ident),+),
2184 $(output: ($($Output:ident),+) ,)?
2185 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2186 bounds: [$($bounds:tt)*],
2187 $(#[$compute_meta:meta])*
2188 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2189 ) => {
2190 #[derive(Debug, Default)]
2191 $(#[$name_meta])*
2192 $vis struct $ModelName;
2193
2194 impl<
2195 $($($other_gen ,)*)?
2196 $($Input),+
2197 $(, $($Output),+)?
2198 > $crate::plugins::MutablePluginModel<
2199 ($($Input),+),
2200 (),
2201 $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?)
2202 > for $ModelName
2203 where
2204 $($bounds)*
2205 {
2206 $(#[$compute_meta])*
2207 fn compute_mut(
2208 &self,
2209 $input_arg: &mut ($($Input),+),
2210 $ctx_arg: &(),
2211 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?) {
2212 $body
2213 }
2214 }
2215 };
2216
2217 // Variant 13: Context, Single Input, Single Output (MUTABLE)
2218 (
2219 $(#[$name_meta:meta])*
2220 name: $vis:vis $ModelName:ident,
2221 input: mut $Input:ident,
2222 $(output: $Output:ident ,)?
2223 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2224 context: $Context:ty,
2225 bounds: [$($bounds:tt)*],
2226 $(#[$compute_meta:meta])*
2227 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2228 ) => {
2229 #[derive(Debug, Default)]
2230 $(#[$name_meta])*
2231 $vis struct $ModelName;
2232
2233 impl<
2234 $($($other_gen ,)*)?
2235 $Input
2236 $(, $Output)?
2237 > $crate::plugins::MutablePluginModel<
2238 $Input,
2239 $Context,
2240 $crate::plugin_model!(@output_ty $Input $(, $Output)?)
2241 > for $ModelName
2242 where
2243 $($bounds)*
2244 {
2245 $(#[$compute_meta])*
2246 fn compute_mut(
2247 &self,
2248 $input_arg: &mut $Input,
2249 $ctx_arg: &$Context
2250 ) -> $crate::plugin_model!(@output_ty $Input $(, $Output)?) {
2251 $body
2252 }
2253 }
2254 };
2255
2256 // Variant 14: Context, Single Input, Tuple Output (MUTABLE)
2257 (
2258 $(#[$name_meta:meta])*
2259 name: $vis:vis $ModelName:ident,
2260 input: mut $Input:ident,
2261 $(output: ($($Output:ident),+) ,)?
2262 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2263 context: $Context:ty,
2264 bounds: [$($bounds:tt)*],
2265 $(#[$compute_meta:meta])*
2266 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2267 ) => {
2268 #[derive(Debug, Default)]
2269 $(#[$name_meta])*
2270 $vis struct $ModelName;
2271
2272 impl<
2273 $($($other_gen ,)*)?
2274 $Input
2275 $(, $($Output),+)?
2276 > $crate::plugins::MutablePluginModel<
2277 $Input,
2278 $Context,
2279 $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?)
2280 > for $ModelName
2281 where
2282 $($bounds)*
2283 {
2284 $(#[$compute_meta])*
2285 fn compute_mut(
2286 &self,
2287 $input_arg: &mut $Input,
2288 $ctx_arg: &$Context
2289 ) -> $crate::plugin_model!(@output_ty $Input $(, ($($Output),+))?) {
2290 $body
2291 }
2292 }
2293 };
2294
2295 // Variant 15: Context, Tuple Input, Single Output (MUTABLE)
2296 (
2297 $(#[$name_meta:meta])*
2298 name: $vis:vis $ModelName:ident,
2299 input: mut ($($Input:ident),+),
2300 $(output: $Output:ident ,)?
2301 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2302 context: $Context:ty,
2303 bounds: [$($bounds:tt)*],
2304 $(#[$compute_meta:meta])*
2305 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2306 ) => {
2307 #[derive(Debug, Default)]
2308 $(#[$name_meta])*
2309 $vis struct $ModelName;
2310
2311 impl<
2312 $($($other_gen ,)*)?
2313 $($Input),+
2314 $(, $Output)?
2315 > $crate::plugins::MutablePluginModel<
2316 ($($Input),+),
2317 $Context,
2318 $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?)
2319 > for $ModelName
2320 where
2321 $($bounds)*
2322 {
2323 $(#[$compute_meta])*
2324 fn compute_mut(
2325 &self,
2326 $input_arg: &mut ($($Input),+),
2327 $ctx_arg: &$Context
2328 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, $Output)?) {
2329 $body
2330 }
2331 }
2332 };
2333
2334 // Variant 16: Context, Tuple Input, Tuple Output (MUTABLE)
2335 (
2336 $(#[$name_meta:meta])*
2337 name: $vis:vis $ModelName:ident,
2338 input: mut ($($Input:ident),+),
2339 $(output: ($($Output:ident),+) ,)?
2340 $(others: [$($other_gen:tt),* $(,)? ] ,)?
2341 context: $Context:ty,
2342 bounds: [$($bounds:tt)*],
2343 $(#[$compute_meta:meta])*
2344 compute: |$input_arg:ident, $ctx_arg:ident| $body:block $(,)?
2345 ) => {
2346 #[derive(Debug, Default)]
2347 $(#[$name_meta])*
2348 $vis struct $ModelName;
2349
2350 impl<
2351 $($($other_gen ,)*)?
2352 $($Input),+
2353 $(, $($Output),+)?
2354 > $crate::plugins::MutablePluginModel<
2355 ($($Input),+),
2356 $Context,
2357 $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?)
2358 > for $ModelName
2359 where
2360 $($bounds)*
2361 {
2362 $(#[$compute_meta])*
2363 fn compute_mut(
2364 &self,
2365 $input_arg: &mut ($($Input),+),
2366 $ctx_arg: &$Context
2367 ) -> $crate::plugin_model!(@output_ty ($($Input),+) $(, ($($Output),+))?) {
2368 $body
2369 }
2370 }
2371
2372 };
2373
2374}
2375
2376// ===============================================================================
2377// ``````````````````````````````` DECLARE FAMILY ````````````````````````````````
2378// ===============================================================================
2379
2380/// Declares a plugin model family using marker types.
2381///
2382/// This macro generates a **family root trait** and one or more **child markers**
2383/// used to represent operations within that family.
2384///
2385/// The generated markers are purely type-level and contain no runtime data.
2386/// Concrete plugin implementations attach to a `(Root, Child)` pair via
2387/// [`plugin_model!`](crate::plugin_model), allowing models to be selected later through context
2388/// and trait bounds.
2389///
2390/// ## Parameters
2391/// - `root`: Declares the **family root trait**.
2392/// - Can include a visibility modifier (`pub`, `pub(crate)`, etc.).
2393/// - The same visibility is applied to all child marker structs.
2394/// - Prefix with `mut` to create a mutable plugin family.
2395/// - `child`: A list of **child marker types** representing operations
2396/// within the family.
2397///
2398/// ## Syntax
2399///
2400/// ```ignore
2401/// declare_family! {
2402/// root: pub FamilyRoot,
2403/// child: [OperationA, OperationB, OperationC]
2404/// }
2405/// ```
2406///
2407/// Mutable families use the `mut` keyword:
2408///
2409/// ```ignore
2410/// declare_family! {
2411/// root: mut pub FamilyRoot,
2412/// child: [OperationA, OperationB]
2413/// }
2414/// ```
2415///
2416/// ## Generated Types
2417///
2418/// The macro generates:
2419///
2420/// - A **child marker struct** for each entry in `child`.
2421/// - A **family root trait** defining associated plugin model types
2422/// for each child operation.
2423///
2424/// The child marker structs are zero-sized types used purely as
2425/// identifiers for operations within the family.
2426///
2427/// The root trait declares an associated model type per child:
2428///
2429/// - Immutable families require models implementing [`PurePluginModel`].
2430/// - Mutable families require models implementing [`MutablePluginModel`].
2431///
2432/// Each associated type corresponds to a concrete plugin implementation
2433/// bound to that operation.
2434///
2435/// These associated model types are later used by [`plugin_model!`](crate::plugin_model) to bind
2436/// concrete implementations to specific `(Root, Child)` combinations.
2437///
2438/// ## Example
2439///
2440/// ```ignore
2441/// declare_family! {
2442/// root: pub VotingFamily,
2443/// child: [Phragmen, STV]
2444/// }
2445/// ```
2446///
2447/// Expands roughly to:
2448///
2449/// ```ignore
2450/// pub struct Phragmen;
2451/// pub struct STV;
2452///
2453/// pub trait VotingFamily<Input, Context, Output> {
2454/// type Phragmen: PurePluginModel<Input, Context, Output>;
2455/// type STV: PurePluginModel<Input, Context, Output>;
2456/// }
2457/// ```
2458///
2459/// Mutable family example:
2460///
2461/// ```ignore
2462/// declare_family! {
2463/// root: mut pub StorageFamily,
2464/// child: [Insert, Remove]
2465/// }
2466/// ```
2467///
2468/// Expands roughly to:
2469///
2470/// ```ignore
2471/// pub struct Insert;
2472/// pub struct Remove;
2473///
2474/// pub trait StorageFamily<Input, Context, Output> {
2475/// type Insert: MutablePluginModel<Input, Context, Output>;
2476/// type Remove: MutablePluginModel<Input, Context, Output>;
2477/// }
2478/// ```
2479#[macro_export]
2480macro_rules! declare_family {
2481 // Immutable Family
2482 (
2483 $(#[$meta:meta])*
2484 root: $vis:vis $Root:ident,
2485 child: [
2486 $(
2487 $(#[$child_meta:meta])*
2488 $Child:ident
2489 ),+ $(,)?
2490 ]
2491 ) => {
2492 $(
2493 $(#[$child_meta])*
2494 $vis struct $Child;
2495 )+
2496
2497 $(#[$meta])*
2498 $vis trait $Root<Input, Context, Output>{
2499 $(
2500 $(#[$child_meta])*
2501 type $Child: $crate::plugins::PurePluginModel<Input, Context, Output>;
2502 )+
2503 }
2504
2505 };
2506
2507 // Mutable Family
2508 (
2509 $(#[$meta:meta])*
2510 root: mut $vis:vis $Root:ident,
2511 child: [
2512 $(
2513 $(#[$child_meta:meta])*
2514 $Child:ident
2515 ),+ $(,)?
2516 ]
2517 ) => {
2518 $(
2519 $(#[$child_meta])*
2520 $vis struct $Child;
2521 )+
2522
2523 $(#[$meta])*
2524 $vis trait $Root<Input, Context, Output> {
2525 $(
2526 $(#[$child_meta])*
2527 type $Child: $crate::plugins::MutablePluginModel<Input, Context, Output>;
2528 )+
2529 }
2530
2531 };
2532}
2533
2534// ===============================================================================
2535// ```````````````````````````````` DEFINE FAMILY ````````````````````````````````
2536// ===============================================================================
2537
2538/// Declares a concrete **plugin family implementation** for a given family root.
2539///
2540/// This macro generates:
2541/// 1. A **family marker struct** representing a concrete implementation of a
2542/// plugin family.
2543/// 2. An implementation of the **family root trait** mapping each declared
2544/// child operation to a concrete plugin model.
2545///
2546/// The generated family struct is purely a **type-level marker** and contains
2547/// no runtime data. It is used to bind concrete plugin models to a specific
2548/// `(FamilyType, Child)` combination through associated types.
2549///
2550/// When `borrow` are specified, the generated family marker stores them
2551/// in `PhantomData` fields so the type system correctly tracks them
2552/// without affecting runtime behavior.
2553///
2554/// ## Syntax
2555///
2556/// ### Family With Context
2557///
2558/// ```ignore
2559/// define_family! {
2560/// root: FamilyRoot, // Required: family root trait
2561///
2562/// family: pub MyFamily, // Required: visibility and concrete family marker struct
2563/// borrow: ['a], // Optional: lifetime parameters for family marker
2564///
2565/// input: Input, // Required: input type parameter
2566/// output: Output, // Optional: output type (defaults to input if omitted)
2567///
2568/// context: MyContext, // Required: context type
2569/// marker: [T], // Optional: generic parameters for the context
2570///
2571/// bounds: [T: Clone], // Optional: trait bounds for the generated impl
2572///
2573/// child: [ // Required: child -> model mapping
2574/// OperationA => ModelA,
2575/// OperationB => ModelB,
2576/// ]
2577/// }
2578/// ```
2579///
2580/// ### Family Without Context
2581///
2582/// ```ignore
2583/// define_family! {
2584/// root: FamilyRoot, // Required: family root trait
2585///
2586/// family: pub MyFamily, // Required: visibility and concrete family marker struct
2587/// borrow: ['a], // Optional: lifetime parameters for family marker
2588///
2589/// input: Input, // Required: input type parameter
2590/// output: Output, // Optional: output type (defaults to input if omitted)
2591///
2592/// bounds: [T: Clone], // Optional: trait bounds for the generated impl
2593///
2594/// child: [ // Required: child -> model mapping
2595/// OperationA => ModelA,
2596/// OperationB => ModelB,
2597/// ]
2598/// }
2599/// ```
2600///
2601/// In the second form, the context parameter of the root trait defaults to `()`.
2602///
2603/// ## Lifetimes and Generics
2604///
2605/// - `borrow` apply to the **family marker type**
2606/// - used to model execution-time borrowing
2607/// - stored via `PhantomData`
2608///
2609/// ```ignore
2610/// borrow: ['a]
2611/// ```
2612///
2613/// - `marker` apply only when a `context` is specified
2614/// - used to parameterize the **context type**
2615/// - introduced on the generated `impl`, not the family struct
2616///
2617/// ```ignore
2618/// context: MyContext,
2619/// marker: [T]
2620/// ```
2621///
2622/// When no `context` is provided:
2623///
2624/// - the context defaults to `()`
2625/// - `marker` is not used
2626///
2627/// ## Example
2628///
2629/// ```ignore
2630///
2631/// // ----- Crate A ------
2632///
2633/// declare_family! {
2634/// root: pub VotingFamily,
2635/// child: [Phragmen, STV]
2636/// }
2637///
2638/// // ----- Crate B ------
2639///
2640/// plugin_model! {
2641/// name: PhragmenModel,
2642/// ...
2643/// }
2644///
2645/// plugin_model! {
2646/// name: STVModel,
2647/// ...
2648/// }
2649///
2650/// define_family! {
2651/// root: VotingFamily,
2652///
2653/// family: pub RuntimeVoting,
2654/// input: AccountId,
2655/// output: Balance,
2656/// context: RuntimeContext,
2657///
2658/// child: [
2659/// Phragmen => PhragmenModel,
2660/// STV => STVModel,
2661/// ]
2662/// }
2663/// ```
2664///
2665/// This binds the `Phragmen` and `Approval` operations of the
2666/// `VotingFamily` root to concrete plugin models for the
2667/// `RuntimeVoting` family implementation.
2668#[macro_export]
2669macro_rules! define_family {
2670
2671 // Helper Rule: `@output_ty`
2672 (@output_ty $Input:tt) => { $Input };
2673 (@output_ty $Input:tt, $Output:tt) => { $Output };
2674
2675
2676 // With Context, Single Input, Single Output
2677 (
2678 root: $Root:ident,
2679 $(#[$meta:meta])*
2680 family: $vis:vis $Name:ident,
2681 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2682 input: $Input:ident,
2683 $(output: $Output:ident ,)?
2684 context: $Context:ty,
2685 $(marker: [$($marker_gen:ident),* $(,)?],)?
2686 $(bounds: [$($bounds:tt)*],)?
2687 child: [
2688 $($child:ident => $model:ty,)+
2689 $(,)? ] $(,)?
2690 ) => {
2691
2692 $crate::__phantom_struct!(
2693 $(#[$meta])*
2694 #[allow(unused)]
2695 $vis
2696 $Name
2697 [$($($borrow_lt),*)?]
2698 []
2699 );
2700
2701 impl<
2702 $($($borrow_lt,)*)?
2703 $($($marker_gen,)*)?
2704 $Input
2705 $(, $Output)?
2706 > $Root<
2707 $Input,
2708 $Context,
2709 $crate::define_family!(@output_ty $Input $(, $Output)?)
2710 >
2711 for $Name$(<
2712 $($borrow_lt,)*
2713 >)?
2714 $(where $($bounds)*)?
2715 {
2716 $(
2717 type $child = $model;
2718 )+
2719 }
2720 };
2721
2722 // With Context, Single Input, Tuple Output
2723 (
2724 root: $Root:ident,
2725 $(#[$meta:meta])*
2726 family: $vis:vis $Name:ident,
2727 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2728 input: $Input:ident,
2729 $(output: ($($Output:ident),+) ,)?
2730 context: $Context:ty,
2731 $(marker: [$($marker_gen:ident),* $(,)?],)?
2732 $(bounds: [$($bounds:tt)*],)?
2733 child: [
2734 $($child:ident => $model:ty,)+
2735 $(,)? ] $(,)?
2736 ) => {
2737
2738 $crate::__phantom_struct!(
2739 $(#[$meta])*
2740 #[allow(unused)]
2741 $vis
2742 $Name
2743 [$($($borrow_lt),*)?]
2744 []
2745 );
2746
2747 impl<
2748 $($($borrow_lt,)*)?
2749 $($($marker_gen,)*)?
2750 $Input
2751 $(, $($Output),+)?
2752 > $Root<
2753 $Input,
2754 $Context,
2755 $crate::define_family!(@output_ty $Input $(, ($($Output),+))?)
2756 >
2757 for $Name$(<
2758 $($borrow_lt,)*
2759 >)?
2760 $(where $($bounds)*)?
2761 {
2762 $(
2763 type $child = $model;
2764 )+
2765
2766 }
2767 };
2768
2769 // With Context, Tuple Input, Single Output
2770 (
2771 root: $Root:ident,
2772 $(#[$meta:meta])*
2773 family: $vis:vis $Name:ident,
2774 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2775 input: ($($Input:ident),+),
2776 $(output: $Output:ident ,)?
2777 context: $Context:ty,
2778 $(marker: [$($marker_gen:ident),* $(,)?],)?
2779 $(bounds: [$($bounds:tt)*],)?
2780 child: [
2781 $($child:ident => $model:ty,)+
2782 $(,)? ] $(,)?
2783 ) => {
2784
2785 $crate::__phantom_struct!(
2786 $(#[$meta])*
2787 #[allow(unused)]
2788 $vis
2789 $Name
2790 [$($($borrow_lt),*)?]
2791 []
2792 );
2793
2794 impl<
2795 $($($borrow_lt,)*)?
2796 $($($marker_gen,)*)?
2797 $($Input),+
2798 $(, $Output)?
2799 > $Root<
2800 ($($Input),+),
2801 $Context,
2802 $crate::define_family!(@output_ty ($($Input),+) $(, $Output)?)
2803 >
2804 for $Name$(<
2805 $($borrow_lt,)*
2806 >)?
2807 $(where $($bounds)*)?
2808 {
2809 $(
2810 type $child = $model;
2811 )+
2812
2813 }
2814 };
2815
2816 // With Context, Tuple Input, Tuple Output
2817 (
2818 root: $Root:ident,
2819 $(#[$meta:meta])*
2820 family: $vis:vis $Name:ident,
2821 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2822 input: ($($Input:ident),+),
2823 $(output: ($($Output:ident),+) ,)?
2824 context: $Context:ty,
2825 $(marker: [$($marker_gen:ident),* $(,)?],)?
2826 $(bounds: [$($bounds:tt)*],)?
2827 child: [
2828 $($child:ident => $model:ty,)+
2829 $(,)? ] $(,)?
2830 ) => {
2831
2832 $crate::__phantom_struct!(
2833 $(#[$meta])*
2834 #[allow(unused)]
2835 $vis
2836 $Name
2837 [$($($borrow_lt),*)?]
2838 []
2839 );
2840
2841 impl<
2842 $($($borrow_lt,)*)?
2843 $($($marker_gen,)*)?
2844 $($Input),+
2845 $(, $($Output),+)?
2846 > $Root<
2847 ($($Input),+),
2848 $Context,
2849 $crate::define_family!(@output_ty ($($Input),+) $(, ($($Output),+))?)
2850 >
2851 for $Name$(<
2852 $($borrow_lt,)*
2853 >)?
2854 $(where $($bounds)*)?
2855 {
2856 $(
2857 type $child = $model;
2858 )+
2859
2860 }
2861 };
2862
2863 // No Context, Single Input, Single Output
2864 (
2865 root: $Root:ident,
2866 $(#[$meta:meta])*
2867 family: $vis:vis $Name:ident,
2868 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2869 input: $Input:ident,
2870 $(output: $Output:ident ,)?
2871 $(bounds: [$($bounds:tt)*],)?
2872 child: [
2873 $($child:ident => $model:ty,)+
2874 $(,)? ] $(,)?
2875 ) => {
2876
2877 $crate::__phantom_struct!(
2878 $(#[$meta])*
2879 #[allow(unused)]
2880 $vis
2881 $Name
2882 [$($($borrow_lt),*)?]
2883 []
2884 );
2885
2886 impl<
2887 $($($borrow_lt,)*)?
2888 $Input
2889 $(, $Output)?
2890 > $Root<
2891 $Input,
2892 (),
2893 $crate::define_family!(@output_ty $Input $(, $Output)?)
2894 >
2895 for $Name$(<
2896 $($borrow_lt,)*
2897 >)?
2898 $(where $($bounds)*)?
2899 {
2900 $(
2901 type $child = $model;
2902 )+
2903 }
2904 };
2905
2906 // No Context, Single Input, Tuple Output
2907 (
2908 root: $Root:ident,
2909 $(#[$meta:meta])*
2910 family: $vis:vis $Name:ident,
2911 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2912 input: $Input:ident,
2913 $(output: ($($Output:ident),+) ,)?
2914 $(bounds: [$($bounds:tt)*],)?
2915 child: [
2916 $($child:ident => $model:ty,)+
2917 $(,)? ] $(,)?
2918 ) => {
2919
2920 $crate::__phantom_struct!(
2921 $(#[$meta])*
2922 #[allow(unused)]
2923 $vis
2924 $Name
2925 [$($($borrow_lt),*)?]
2926 []
2927 );
2928
2929 impl<
2930 $($($borrow_lt,)*)?
2931 $Input
2932 $(, $($Output),+)?
2933 > $Root<
2934 $Input,
2935 (),
2936 $crate::define_family!(@output_ty $Input $(, ($($Output),+))?)
2937 >
2938 for $Name$(<
2939 $($borrow_lt,)*
2940 >)?
2941 $(where $($bounds)*)?
2942 {
2943 $(
2944 type $child = $model;
2945 )+
2946 }
2947 };
2948
2949 // No Context, Tuple Input, Single Output
2950 (
2951 root: $Root:ident,
2952 $(#[$meta:meta])*
2953 family: $vis:vis $Name:ident,
2954 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2955 input: ($($Input:ident),+),
2956 $(output: $Output:ident ,)?
2957 $(bounds: [$($bounds:tt)*],)?
2958 child: [
2959 $($child:ident => $model:ty,)+
2960 $(,)? ] $(,)?
2961 ) => {
2962
2963 $crate::__phantom_struct!(
2964 $(#[$meta])*
2965 #[allow(unused)]
2966 $vis
2967 $Name
2968 [$($($borrow_lt),*)?]
2969 []
2970 );
2971
2972 impl<
2973 $($($borrow_lt,)*)?
2974 $($Input),+
2975 $(, $Output)?
2976 > $Root<
2977 ($($Input),+),
2978 (),
2979 $crate::define_family!(@output_ty ($($Input),+) $(, $Output)?)
2980 >
2981 for $Name$(<
2982 $($borrow_lt,)*
2983 >)?
2984 $(where $($bounds)*)?
2985 {
2986 $(
2987 type $child = $model;
2988 )+
2989 }
2990 };
2991
2992 // No Context, Tuple Input, Tuple Output
2993 (
2994 root: $Root:ident,
2995 $(#[$meta:meta])*
2996 family: $vis:vis $Name:ident,
2997 $(borrow: [$($borrow_lt:lifetime),* $(,)?],)?
2998 input: ($($Input:ident),+),
2999 $(output: ($($Output:ident),+) ,)?
3000 $(bounds: [$($bounds:tt)*],)?
3001 child: [
3002 $($child:ident => $model:ty,)+
3003 $(,)? ] $(,)?
3004 ) => {
3005
3006 $crate::__phantom_struct!(
3007 $(#[$meta])*
3008 #[allow(unused)]
3009 $vis
3010 $Name
3011 [$($($borrow_lt),*)?]
3012 []
3013 );
3014
3015 impl<
3016 $($($borrow_lt,)*)?
3017 $($Input),+
3018 $(, $($Output),+)?
3019 > $Root<
3020 ($($Input),+),
3021 (),
3022 $crate::define_family!(@output_ty ($($Input),+) $(, ($($Output),+))?)
3023 >
3024 for $Name$(<
3025 $($borrow_lt,)*
3026 >)?
3027 $(where $($bounds)*)?
3028 {
3029 $(
3030 type $child = $model;
3031 )+
3032 }
3033 };
3034
3035}
3036
3037// ===============================================================================
3038// ```````````````````````````````` HELPER MACROS ````````````````````````````````
3039// ===============================================================================
3040
3041/// Generates a zero-sized or PhantomData-backed marker struct.
3042///
3043/// This is an internal helper used by `plugin_context`, `define_family`,
3044/// and other macros that need to produce marker structs which may carry
3045/// lifetime or type parameters purely at the type level without any
3046/// runtime storage.
3047///
3048/// ## Syntax
3049///
3050/// ```ignore
3051/// __phantom_struct!(
3052/// #[attributes] // optional
3053/// VISIBILITY // pub, pub(crate), or empty
3054/// NAME // struct identifier
3055/// [LIFETIMES] // e.g. ['a, 'b] or []
3056/// [GENERICS] // e.g. [T, U] or []
3057/// )
3058/// ```
3059///
3060/// ## Variants
3061///
3062/// | Lifetimes | Generics | Generated struct |
3063/// |-----------|----------|------------------------------------------------|
3064/// | `[]` | `[]` | `struct Foo;` |
3065/// | `['a]` | `[]` | `struct Foo<'a>(PhantomData<(&'a (),)>)` |
3066/// | `[]` | `[T]` | `struct Foo<T>(PhantomData<(T,)>)` |
3067/// | `['a]` | `[T]` | `struct Foo<'a, T>(PhantomData<(T, &'a ())>)` |
3068///
3069#[macro_export]
3070macro_rules! __phantom_struct {
3071
3072 // Arm 1: No lifetimes, no generics -> plain unit struct.
3073 (
3074 $(#[$meta:meta])*
3075 $vis:vis
3076 $Name:ident
3077 []
3078 []
3079 ) => {
3080 $(#[$meta])*
3081 $vis struct $Name;
3082 };
3083
3084 // Arm 2: Lifetimes only -> struct with a PhantomData reference tuple
3085 // that is covariant over each declared lifetime independently.
3086 (
3087 $(#[$meta:meta])*
3088 $vis:vis
3089 $Name:ident
3090 [$($lt:lifetime),+ $(,)?]
3091 []
3092 ) => {
3093 $(#[$meta])*
3094 $vis struct $Name<$($lt),*>(
3095 core::marker::PhantomData<($(&$lt (),)*)>
3096 );
3097 };
3098
3099 // Arm 3: Generics only -> struct with a PhantomData tuple field that
3100 // tracks each type parameter independently.
3101 (
3102 $(#[$meta:meta])*
3103 $vis:vis
3104 $Name:ident
3105 []
3106 [$($gen:ident),+ $(,)?]
3107 ) => {
3108 $(#[$meta])*
3109 $vis struct $Name<$($gen),*>(
3110 core::marker::PhantomData<($($gen,)*)>
3111 );
3112 };
3113
3114 // Arm 4: Both lifetimes and generics -> single PhantomData tuple field
3115 // combining both, so the struct has one field instead of two and the
3116 // variance of each parameter remains independent.
3117 (
3118 $(#[$meta:meta])*
3119 $vis:vis
3120 $Name:ident
3121 [$($lt:lifetime),+ $(,)?]
3122 [$($gen:ident),+ $(,)?]
3123 ) => {
3124 $(#[$meta])*
3125 $vis struct $Name<$($lt),*, $($gen),*>(
3126 core::marker::PhantomData<($($gen,)* $(&$lt (),)*)>
3127 );
3128 };
3129}
3130
3131// ===============================================================================
3132// `````````````````````````````````` MOCK TEST ``````````````````````````````````
3133// ===============================================================================
3134
3135#[cfg(test)]
3136#[allow(unused)]
3137mod tests {
3138
3139 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3140 // ``````````````````````````````````` IMPORTS ```````````````````````````````````
3141 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3142
3143 // --- Local crate imports ---
3144 use super::*;
3145
3146 // --- Core / Std ---
3147 use core::marker::PhantomData;
3148 use std::mem::take;
3149
3150 // --- Substrate primitives ---
3151 use sp_arithmetic::traits::AtLeast8BitUnsigned;
3152
3153 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3154 // ``````````````````````````````````` STRUCTS ```````````````````````````````````
3155 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3156
3157 //--- Mock structs ---
3158
3159 #[derive(Debug, Clone, PartialEq, Eq)]
3160 pub struct BasicConfig {
3161 value: u8,
3162 }
3163
3164 #[derive(Debug, Clone, PartialEq, Eq)]
3165 pub struct GenericConfig<T> {
3166 value: T,
3167 }
3168
3169 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3170 // ``````````````````````````````` PLUGIN CONTEXT ````````````````````````````````
3171 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3172
3173 //--- Basic context form ---
3174
3175 plugin_context! {
3176 name: pub BasicConfigProvider,
3177 context: BasicConfig,
3178 value: BasicConfig {value: 10}
3179 }
3180
3181 #[test]
3182 fn plugin_context_basic_form_returns_expected_context() {
3183 let ctx = <BasicConfigProvider as ModelContext>::context();
3184 assert_eq!(ctx, BasicConfig { value: 10 });
3185 }
3186
3187 //--- Generic marker form ---
3188
3189 plugin_context! {
3190 name: GenericConfigProvider,
3191 context: GenericConfig<T>,
3192 marker: [T],
3193 bounds: [T: AtLeast8BitUnsigned + Default],
3194 value: GenericConfig {value: T::default()}
3195 }
3196
3197 #[test]
3198 fn plugin_context_marker_form_returns_expected_context() {
3199 let ctx = <GenericConfigProvider<u8> as ModelContext>::context();
3200 assert_eq!(ctx, GenericConfig { value: 0u8 });
3201 }
3202
3203 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3204 // ```````````````````````````````` PLUGIN MODEL `````````````````````````````````
3205 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3206
3207 //--- Variant 1: No Context, Single Input, Single Output ---
3208
3209 plugin_model! {
3210 name: pub PureNoCtxSingleSingle,
3211 input: Input,
3212 output: Output,
3213 bounds: [Input: Into<u8>, Output: From<u8>],
3214 compute: |input, _ctx| {
3215 let x = input.into();
3216 Output::from(x + 1)
3217 }
3218 }
3219
3220 plugin_test! {
3221 model: PureNoCtxSingleSingle,
3222 input: u8,
3223 output: u8,
3224 cases: {
3225 (pure_no_ctx_single_single_case1, 10, 11),
3226 (pure_no_ctx_single_single_case2, 0, 1),
3227 }
3228 }
3229
3230 plugin_test! {
3231 model: PureNoCtxSingleSingle,
3232 input: u8,
3233 cases: {
3234 (pure_no_ctx_single_single_case3, 99, 100),
3235 (pure_no_ctx_single_single_case4, 24, 25),
3236 }
3237 }
3238
3239 //--- Variant 2: No Context, Single Input, Tuple Output ---
3240
3241 plugin_model! {
3242 name: pub PureNoCtxSingleTuple,
3243 input: Input,
3244 output: (OutA, OutB),
3245 bounds: [Input: Into<u8>, OutA: From<u8>, OutB: From<u8>],
3246 compute: |input, _ctx| {
3247 let x = input.into();
3248 (OutA::from(x), OutB::from(x + 1))
3249 }
3250 }
3251
3252 plugin_test! {
3253 model: PureNoCtxSingleTuple,
3254 input: u8,
3255 output: (u8, u8),
3256 cases: {
3257 (pure_no_ctx_single_tuple_case1, 10, (10, 11)),
3258 (pure_no_ctx_single_tuple_case2, 0, (0, 1)),
3259 }
3260 }
3261
3262 //--- Variant 3: No Context, Tuple Input, Single Output ---
3263
3264 plugin_model! {
3265 name: pub PureNoCtxTupleSingle,
3266 input: (InpA, InpB),
3267 output: Output,
3268 bounds: [InpA: Into<u8>, InpB: Into<u8>, Output: From<u8>],
3269 compute: |input, _ctx| {
3270 let (a, b) = input;
3271 Output::from(a.into() + b.into())
3272 }
3273 }
3274
3275 plugin_test! {
3276 model: PureNoCtxTupleSingle,
3277 input: (u8, u8),
3278 output: u8,
3279 cases: {
3280 (pure_no_ctx_tuple_single_case1, (10, 11), 21),
3281 (pure_no_ctx_tuple_single_case2, (0, 5), 5),
3282 }
3283 }
3284
3285 //--- Variant 4: No Context, Tuple Input, Tuple Output ---
3286
3287 plugin_model! {
3288 name: pub PureNoCtxTupleTuple,
3289 input: (InpA, InpB),
3290 output: (OutA, OutB),
3291 bounds: [InpA: Into<u8>, InpB: Into<u8>, OutA: From<u8>, OutB: From<u8>],
3292 compute: |input, _ctx| {
3293 let (a, b) = input;
3294 (OutA::from(a.into() * 10), OutB::from(b.into() * 10))
3295 }
3296 }
3297
3298 plugin_test! {
3299 model: PureNoCtxTupleTuple,
3300 input: (u8, u8),
3301 output: (u8, u8),
3302 cases: {
3303 (pure_no_ctx_tuple_tuple_case1, (5, 10), (50, 100)),
3304 (pure_no_ctx_tuple_tuple_case2, (1, 0), (10, 0)),
3305 }
3306 }
3307
3308 // --- Variant 5: Context, Single Input, Single Output ---
3309
3310 plugin_model! {
3311 name: pub PureCtxSingleSingle,
3312 input: Input,
3313 output: Output,
3314 context: BasicConfig,
3315 bounds: [Input: Into<u8>, Output: From<u8>],
3316 compute: |input, ctx| {
3317 let v = ctx.value;
3318 Output::from(input.into() + v)
3319 }
3320 }
3321
3322 plugin_test! {
3323 model: PureCtxSingleSingle,
3324 input: u8,
3325 output: u8,
3326 context: BasicConfig,
3327 value: BasicConfig{ value: 10 },
3328 cases: {
3329 (pure_ctx_single_single_case1, 10, 20),
3330 (pure_ctx_single_single_case2, 1, 11),
3331 }
3332 }
3333
3334 plugin_test! {
3335 model: PureCtxSingleSingle,
3336 input: u8,
3337 context: BasicConfig,
3338 value: BasicConfig{ value: 10 },
3339 cases: {
3340 (pure_ctx_single_single_case3, 90, 100),
3341 (pure_ctx_single_single_case4, 0, 10),
3342 }
3343 }
3344
3345 //--- Variant 6: Context, Single Input, Tuple Output ---
3346
3347 plugin_model! {
3348 name: pub PureCtxSingleTuple,
3349 input: Input,
3350 output: (OutA, OutB),
3351 context: BasicConfig,
3352 bounds: [Input: Into<u8>, OutA: From<u8>, OutB: From<u8>],
3353 compute: |input, ctx| {
3354 let x = input.into();
3355 let v = ctx.value;
3356 (OutA::from(x), OutB::from(x + v))
3357 }
3358 }
3359
3360 plugin_test! {
3361 model: PureCtxSingleTuple,
3362 input: u8,
3363 output: (u8, u8),
3364 context: BasicConfig,
3365 value: BasicConfig{ value: 1 },
3366 cases: {
3367 (pure_ctx_single_tuple_case1, 5, (5, 6)),
3368 (pure_ctx_single_tuple_case2, 1, (1, 2)),
3369 }
3370 }
3371
3372 //--- Variant 7: Context, Tuple Input, Single Output ---
3373
3374 plugin_model! {
3375 name: pub PureCtxTupleSingle,
3376 input: (InpA, InpB),
3377 output: Output,
3378 context: BasicConfig,
3379 bounds: [InpA: Into<u8>, InpB: Into<u8>, Output: From<u8>],
3380 compute: |input, ctx| {
3381 let (a, b) = input;
3382 let v = ctx.value;
3383 Output::from(a.into() + b.into() + v)
3384 }
3385 }
3386
3387 plugin_test! {
3388 model: PureCtxTupleSingle,
3389 input: (u8, u8),
3390 output: u8,
3391 context: BasicConfig,
3392 value: BasicConfig { value: 10},
3393 cases: {
3394 (pure_ctx_tuple_single_case1, (10, 10), 30),
3395 (pure_ctx_tuple_single_case2, (5, 30), 45),
3396 }
3397 }
3398
3399 //--- Variant 8: Context, Tuple Input, Tuple Output ---
3400
3401 plugin_model! {
3402 name: pub PureCtxTupleTuple,
3403 input: (InpA, InpB),
3404 output: (OutA, OutB),
3405 context: BasicConfig,
3406 bounds: [InpA: Into<u8>, InpB: Into<u8>, OutA: From<u8>, OutB: From<u8>],
3407 compute: |input, ctx| {
3408 let (a, b) = input;
3409 let v = ctx.value;
3410 (OutA::from(a.into() + v), OutB::from(b.into() + v))
3411 }
3412 }
3413
3414 plugin_test! {
3415 model: PureCtxTupleTuple,
3416 input: (u8, u8),
3417 output: (u8, u8),
3418 context: BasicConfig,
3419 value: BasicConfig { value: 5 },
3420 cases: {
3421 (pure_ctx_tuple_tuple_case1, (5, 10), (10, 15)),
3422 (pure_ctx_tuple_tuple_case2, (1, 0), (6, 5)),
3423 }
3424 }
3425
3426 //--- Variant 9: No Context, Single Input, Single Output (MUTABLE) ---
3427
3428 plugin_model! {
3429 name: pub MutNoCtxSingleSingle,
3430 input: mut Input,
3431 output: Output,
3432 bounds: [Input: From<Vec<u8>> + Into<Vec<u8>> + Default, Output: From<usize>],
3433 compute: |input, _ctx| {
3434 let mut v: Vec<u8> = take(input).into();
3435 v.push(1);
3436 let len = v.len();
3437 *input = Input::from(v);
3438 Output::from(len)
3439 }
3440 }
3441
3442 plugin_test! {
3443 model: MutNoCtxSingleSingle,
3444 input: mut Vec<u8>,
3445 output: usize,
3446 cases: {
3447 (mut_no_ctx_single_single_case1, vec![1, 2], 3usize, vec![1, 2, 1]),
3448 (mut_no_ctx_single_single_case2, vec![5, 4, 3, 2], 5usize, vec![5, 4, 3, 2, 1]),
3449 }
3450 }
3451
3452 //--- Variant 10: No Context, Single Input, Tuple Output (MUTABLE) ---
3453
3454 plugin_model! {
3455 name: pub MutNoCtxSingleTuple,
3456 input: mut Input,
3457 output: (OutA, OutB),
3458 bounds: [Input: From<Vec<u8>> + Into<Vec<u8>> + Default, OutA: From<usize>, OutB: From<u8>],
3459 compute: |input, _ctx| {
3460 let mut v: Vec<u8> = take(input).into();
3461 v.push(1);
3462 v.push(0);
3463 let len = v.len();
3464 let last = *v.last().unwrap();
3465 *input = Input::from(v);
3466 (OutA::from(len), OutB::from(last))
3467 }
3468 }
3469
3470 plugin_test! {
3471 model: MutNoCtxSingleTuple,
3472 input: mut Vec<u8>,
3473 output: (usize, u8),
3474 cases: {
3475 (mut_no_ctx_single_tuple_case1, vec![1, 0], (4usize, 0), vec![1, 0, 1, 0]),
3476 (mut_no_ctx_single_tuple_case2, vec![5, 4, 3, 2], (6usize, 0)),
3477 }
3478 }
3479
3480 //--- Variant 11: No Context, Tuple Input, Single Output (MUTABLE) ---
3481
3482 plugin_model! {
3483 name: pub MutNoCtxTupleSingle,
3484 input: mut (InpA, InpB),
3485 output: Output,
3486 bounds: [InpA: From<u8> + Into<u8> + Copy, InpB: From<u8> + Into<u8> + Copy, Output: From<u8>],
3487 compute: |input, _ctx| {
3488 input.0 = InpA::from(input.0.into() + 1);
3489 input.1 = InpB::from(input.1.into() + 1);
3490 Output::from(input.0.into() + input.1.into())
3491 }
3492 }
3493
3494 plugin_test! {
3495 model: MutNoCtxTupleSingle,
3496 input: mut (u8, u8),
3497 output: u8,
3498 cases: {
3499 (mut_no_ctx_tuple_single_case1, (11, 12), 25, (12, 13)),
3500 (mut_no_ctx_tuple_single_case2, (0, 0), 2, (1, 1)),
3501
3502 }
3503 }
3504
3505 //--- Variant 12: No Context, Tuple Input, Tuple Output (MUTABLE) ---
3506
3507 plugin_model! {
3508 name: pub MutNoCtxTupleTuple,
3509 input: mut (A, B),
3510 output: (OutA, OutB),
3511 bounds: [A: From<u8> + Into<u8> + Copy, B: From<u8> + Into<u8> + Copy, OutA: From<u8>, OutB: From<u8>],
3512 compute: |input, _ctx| {
3513 input.0 = A::from(input.0.into() * 10);
3514 input.1 = B::from(input.1.into() * 0);
3515 (OutA::from(input.0.into()), OutB::from(input.1.into()))
3516 }
3517 }
3518
3519 plugin_test! {
3520 model: MutNoCtxTupleTuple,
3521 input: mut (u8, u8),
3522 output: (u8, u8),
3523 cases: {
3524 (mut_no_ctx_tuple_tuple_case1, (10, 10), (100, 0), (100, 0)),
3525 (mut_no_ctx_tuple_tuple_case2, (20, 35), (200, 0)),
3526 }
3527 }
3528
3529 plugin_test! {
3530 model: MutNoCtxTupleTuple,
3531 input: mut (u8, u8),
3532 cases: {
3533 (mut_no_ctx_tuple_tuple_case3, (1, 10), (10, 0), (10, 0)),
3534 (mut_no_ctx_tuple_tuple_case4, (0, 0), (0, 0)),
3535 }
3536 }
3537
3538 //--- Variant 13: Context, Single Input, Single Output (MUTABLE) ---
3539
3540 plugin_model! {
3541 name: pub MutCtxSingleSingle,
3542 input: mut Input,
3543 output: Output,
3544 context: BasicConfig,
3545 bounds: [Input: From<Vec<u8>> + Into<Vec<u8>> + Default, Output: From<usize>],
3546 compute: |input, ctx| {
3547 let mut v: Vec<u8> = take(input).into();
3548 v.push(ctx.value);
3549 let len = v.len();
3550 *input = Input::from(v);
3551 Output::from(len)
3552 }
3553 }
3554
3555 plugin_test! {
3556 model: MutCtxSingleSingle,
3557 input: mut Vec<u8>,
3558 output: usize,
3559 context: BasicConfig,
3560 value: BasicConfig { value: 0 },
3561 cases: {
3562 (mut_ctx_single_single_case1, vec![1], 2usize, vec![1, 0]),
3563 (mut_ctx_single_single_case2, vec![1, 5, 3], 4usize, vec![1, 5, 3, 0]),
3564
3565 }
3566 }
3567
3568 //--- Variant 14: Context, Single Input, Tuple Output (MUTABLE) --
3569
3570 plugin_model! {
3571 name: pub MutCtxSingleTuple,
3572 input: mut Input,
3573 output: (OutA, OutB),
3574 context: BasicConfig,
3575 bounds: [Input: From<Vec<u8>> + Into<Vec<u8>> + Default, OutA: From<usize>, OutB: From<u8>],
3576 compute: |input, ctx| {
3577 let mut v: Vec<u8> = take(input).into();
3578 v.push(ctx.value);
3579 let len = v.len();
3580 let last = *v.last().unwrap();
3581 *input = Input::from(v);
3582 (OutA::from(len), OutB::from(last))
3583 }
3584 }
3585
3586 plugin_test! {
3587 model: MutCtxSingleTuple,
3588 input: mut Vec<u8>,
3589 output: (usize, u8),
3590 context: BasicConfig,
3591 value: BasicConfig{value: 4},
3592 cases: {
3593 (mut_ctx_single_tuple_case1, vec![2, 3], (3usize, 4), vec![2, 3, 4]),
3594 (mut_ctx_single_tuple_case2, vec![20, 16, 12, 8], (5usize, 4), vec![20, 16, 12, 8, 4]),
3595 }
3596 }
3597
3598 //--- Variant 15: Context, Tuple Input, Single Output (MUTABLE) ---
3599
3600 plugin_model! {
3601 name: pub MutCtxTupleSingle,
3602 input: mut (InpA, InpB),
3603 output: Output,
3604 context: BasicConfig,
3605 bounds: [InpA: From<u8> + Into<u8> + Copy, InpB: From<u8> + Into<u8> + Copy, Output: From<u8>],
3606 compute: |input, ctx| {
3607 input.0 = InpA::from(input.0.into() + ctx.value);
3608 input.1 = InpB::from(input.1.into() + ctx.value);
3609 Output::from(input.0.into() + input.1.into())
3610 }
3611 }
3612
3613 plugin_test! {
3614 model: MutCtxTupleSingle,
3615 input: mut (u8, u8),
3616 output: u8,
3617 context: BasicConfig,
3618 value: BasicConfig{value: 2},
3619 cases: {
3620 (mut_ctx_tuple_single_case1, (1, 2), 7, (3, 4)),
3621 (mut_ctx_tuple_single_case2, (8, 8), 20, (10, 10)),
3622 }
3623 }
3624
3625 //--- Variant 16: Context, Tuple Input, Tuple Output (MUTABLE) ---
3626
3627 plugin_model! {
3628 name: pub MutCtxTupleTuple,
3629 input: mut (A, B),
3630 output: (OutA, OutB),
3631 context: BasicConfig,
3632 bounds: [A: From<u8> + Into<u8> + Copy, B: From<u8> + Into<u8> + Copy, OutA: From<u8>, OutB: From<u8>],
3633 compute: |input, ctx| {
3634 input.0 = A::from(input.0.into() + ctx.value);
3635 input.1 = B::from(input.1.into() + ctx.value + 1);
3636 (OutA::from(input.0.into()), OutB::from(input.1.into()))
3637 }
3638 }
3639
3640 plugin_test! {
3641 model: MutCtxTupleTuple,
3642 input: mut (u8, u8),
3643 output: (u8, u8),
3644 context: BasicConfig,
3645 value: BasicConfig{value: 2},
3646 cases: {
3647 (mut_ctx_tuple_tuple_case1, (5, 6), (7, 9), (7, 9)),
3648 (mut_ctx_tuple_tuple_case2, (0, 0), (2, 3), (2, 3)),
3649 }
3650 }
3651
3652 plugin_test! {
3653 model: MutCtxTupleTuple,
3654 input: mut (u8, u8),
3655 context: BasicConfig,
3656 value: BasicConfig{value: 2},
3657 cases: {
3658 (mut_ctx_tuple_tuple_case3, (8, 7), (10, 10), (10, 10)),
3659 (mut_ctx_tuple_tuple_case4, (1, 1), (3, 4), (3, 4)),
3660 }
3661 }
3662
3663 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3664 // ````````````````````````````````` PLUGIN TYPES ````````````````````````````````
3665 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3666
3667 plugin_context! {
3668 name: UnitContextProvider,
3669 context: (),
3670 value: ()
3671 }
3672
3673 //--- Variant 1: Immutable concrete model arm ---
3674
3675 trait ImmutablePluginTrait {
3676 plugin_types! {
3677 input: u8,
3678 output: u8,
3679 model: Model,
3680 context: Context,
3681 }
3682 }
3683
3684 struct ImmutableHost;
3685
3686 impl ImmutablePluginTrait for ImmutableHost {
3687 type Model = PureNoCtxSingleSingle;
3688 type Context = UnitContextProvider;
3689 }
3690
3691 fn run_immutable_plugin<T: ImmutablePluginTrait>(input: u8) -> u8 {
3692 let model = T::Model::default();
3693 let ctx = <T::Context as ModelContext>::context();
3694
3695 <T::Model as PurePluginModel<u8, <T::Context as ModelContext>::Context, u8>>::compute(
3696 &model, input, &ctx,
3697 )
3698 }
3699
3700 #[test]
3701 fn plugin_types_immutable_model_arm_works() {
3702 assert_eq!(run_immutable_plugin::<ImmutableHost>(10), 11);
3703 assert_eq!(run_immutable_plugin::<ImmutableHost>(0), 1);
3704 }
3705
3706 //--- Variant 2: Mutable concrete model arm ---
3707
3708 trait MutablePluginTrait {
3709 plugin_types! {
3710 input: mut Vec<u8>,
3711 output: usize,
3712 model: Model,
3713 context: Context,
3714 }
3715 }
3716
3717 struct MutableHost;
3718
3719 impl MutablePluginTrait for MutableHost {
3720 type Model = MutNoCtxSingleSingle;
3721 type Context = UnitContextProvider;
3722 }
3723
3724 fn run_mutable_plugin<T: MutablePluginTrait>(mut input: Vec<u8>) -> (Vec<u8>, usize) {
3725 let model = T::Model::default();
3726 let ctx = <T::Context as ModelContext>::context();
3727
3728 let out = <T::Model as MutablePluginModel<
3729 Vec<u8>,
3730 <T::Context as ModelContext>::Context,
3731 usize,
3732 >>::compute_mut(&model, &mut input, &ctx);
3733
3734 (input, out)
3735 }
3736
3737 #[test]
3738 fn plugin_types_mutable_model_arm_works() {
3739 let (input, out) = run_mutable_plugin::<MutableHost>(vec![1, 2]);
3740 assert_eq!(input, vec![1, 2, 1]);
3741 assert_eq!(out, 3);
3742
3743 let (input, out) = run_mutable_plugin::<MutableHost>(vec![5, 4, 3, 2]);
3744 assert_eq!(input, vec![5, 4, 3, 2, 1]);
3745 assert_eq!(out, 5);
3746 }
3747
3748 //--- Variant 3: Family arm ---
3749
3750 trait SimpleFamilyRoot<Input, Context, Output> {
3751 type Op: PurePluginModel<Input, Context, Output> + Default;
3752 }
3753
3754 struct SimpleFamily;
3755
3756 impl SimpleFamilyRoot<u8, (), u8> for SimpleFamily {
3757 type Op = PureNoCtxSingleSingle;
3758 }
3759
3760 trait FamilyPluginTrait {
3761 plugin_types! {
3762 input: u8,
3763 output: u8,
3764 root: SimpleFamilyRoot,
3765 family: Family,
3766 context: Context,
3767 }
3768 }
3769
3770 struct FamilyHost;
3771
3772 impl FamilyPluginTrait for FamilyHost {
3773 type Family = SimpleFamily;
3774 type Context = UnitContextProvider;
3775 }
3776
3777 fn run_family_plugin<T: FamilyPluginTrait>(input: u8) -> u8 {
3778 let model = <T::Family as SimpleFamilyRoot<
3779 u8,
3780 <T::Context as ModelContext>::Context,
3781 u8,
3782 >>::Op::default();
3783
3784 let ctx = <T::Context as ModelContext>::context();
3785
3786 <<T::Family as SimpleFamilyRoot<
3787 u8,
3788 <T::Context as ModelContext>::Context,
3789 u8
3790 >>::Op as PurePluginModel<
3791 u8,
3792 <T::Context as ModelContext>::Context,
3793 u8
3794 >>::compute(&model, input, &ctx)
3795 }
3796
3797 #[test]
3798 fn plugin_types_family_arm_works() {
3799 assert_eq!(run_family_plugin::<FamilyHost>(10), 11);
3800 assert_eq!(run_family_plugin::<FamilyHost>(0), 1);
3801 }
3802
3803 //--- Varaint 4: Family arm with `provides` ---
3804
3805 trait ProvidedFamilyRoot<Input, Context, Output> {
3806 type Op: PurePluginModel<Input, Context, Output> + Default;
3807 }
3808
3809 struct ProvidedFamily;
3810
3811 impl ProvidedFamilyRoot<u8, BasicConfig, u8> for ProvidedFamily {
3812 type Op = PureCtxSingleSingle;
3813 }
3814
3815 trait ProvidedFamilyPluginTrait {
3816 plugin_types! {
3817 input: u8,
3818 output: u8,
3819 root: ProvidedFamilyRoot,
3820 family: Family,
3821 context: Context,
3822 provides: [Send + Sync + 'static],
3823 }
3824 }
3825
3826 struct ThreadSafeConfigProvider;
3827
3828 impl ModelContext for ThreadSafeConfigProvider {
3829 type Context = BasicConfig;
3830
3831 fn context() -> Self::Context {
3832 BasicConfig { value: 10 }
3833 }
3834 }
3835
3836 struct ProvidedFamilyHost;
3837
3838 impl ProvidedFamilyPluginTrait for ProvidedFamilyHost {
3839 type Family = ProvidedFamily;
3840 type Context = ThreadSafeConfigProvider;
3841 }
3842
3843 fn run_family_with_provides<T: ProvidedFamilyPluginTrait>(input: u8) -> u8 {
3844 let model = <T::Family as ProvidedFamilyRoot<
3845 u8,
3846 <T::Context as ModelContext>::Context,
3847 u8,
3848 >>::Op::default();
3849
3850 let ctx = <T::Context as ModelContext>::context();
3851
3852 <<T::Family as ProvidedFamilyRoot<
3853 u8,
3854 <T::Context as ModelContext>::Context,
3855 u8
3856 >>::Op as PurePluginModel<
3857 u8,
3858 <T::Context as ModelContext>::Context,
3859 u8
3860 >>::compute(&model, input, &ctx)
3861 }
3862
3863 #[test]
3864 fn plugin_types_family_arm_with_provides_works() {
3865 assert_eq!(run_family_with_provides::<ProvidedFamilyHost>(10), 20);
3866 assert_eq!(run_family_with_provides::<ProvidedFamilyHost>(1), 11);
3867 }
3868
3869 //--- Variant 5: Family arm with `borrow`
3870
3871 #[derive(Default)]
3872 struct BorrowIdentityModel;
3873
3874 impl<'a> PurePluginModel<&'a [u8], (), &'a [u8]> for BorrowIdentityModel {
3875 fn compute(&self, input: &'a [u8], _context: &()) -> &'a [u8] {
3876 input
3877 }
3878 }
3879
3880 trait BorrowFamilyRoot<Input, Context, Output> {
3881 type Op: PurePluginModel<Input, Context, Output> + Default;
3882 }
3883
3884 struct BorrowFamily<'a>(PhantomData<&'a ()>);
3885
3886 impl<'a> BorrowFamilyRoot<&'a [u8], (), &'a [u8]> for BorrowFamily<'a> {
3887 type Op = BorrowIdentityModel;
3888 }
3889
3890 trait BorrowedFamilyPluginTrait {
3891 plugin_types! {
3892 input: &'a [u8],
3893 output: &'a [u8],
3894 borrow: ['a],
3895 root: BorrowFamilyRoot,
3896 family: Family,
3897 context: Context,
3898 }
3899 }
3900
3901 struct BorrowedFamilyHost;
3902
3903 impl BorrowedFamilyPluginTrait for BorrowedFamilyHost {
3904 type Family<'a> = BorrowFamily<'a>;
3905 type Context = UnitContextProvider;
3906 }
3907
3908 fn run_borrowed_family_plugin<'a, T: BorrowedFamilyPluginTrait>(input: &'a [u8]) -> &'a [u8] {
3909 let model = <T::Family<'a> as BorrowFamilyRoot<
3910 &'a [u8],
3911 <T::Context as ModelContext>::Context,
3912 &'a [u8],
3913 >>::Op::default();
3914
3915 let ctx = <T::Context as ModelContext>::context();
3916
3917 <<T::Family<'a> as BorrowFamilyRoot<
3918 &'a [u8],
3919 <T::Context as ModelContext>::Context,
3920 &'a [u8]
3921 >>::Op as PurePluginModel<
3922 &'a [u8],
3923 <T::Context as ModelContext>::Context,
3924 &'a [u8]
3925 >>::compute(&model, input, &ctx)
3926 }
3927
3928 #[test]
3929 fn plugin_types_family_arm_with_borrow_works() {
3930 let data = [1u8, 2, 3];
3931 assert_eq!(
3932 run_borrowed_family_plugin::<BorrowedFamilyHost>(&data),
3933 &data
3934 );
3935 }
3936
3937 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3938 // ```````````````````````````````` PLUGIN OUTPUT ````````````````````````````````
3939 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3940
3941 // Variant 1: Immutable concrete model arm
3942
3943 struct OutputPureModelRunner;
3944
3945 impl OutputPureModelRunner {
3946 plugin_output! {
3947 pub fn run_pure_model,
3948 input: u8,
3949 output: u8,
3950 model: PureCtxSingleSingle,
3951 context: BasicConfigProvider,
3952 }
3953 }
3954
3955 #[test]
3956 fn plugin_output_immutable_model_arm_works() {
3957 assert_eq!(OutputPureModelRunner::run_pure_model(10), 20);
3958 assert_eq!(OutputPureModelRunner::run_pure_model(0), 10);
3959 }
3960
3961 // Variant 2: concrete model arm
3962
3963 struct OutputMutableModelRunner;
3964
3965 impl OutputMutableModelRunner {
3966 plugin_output! {
3967 pub fn run_mutable_model,
3968 input: mut Vec<u8>,
3969 output: usize,
3970 model: MutNoCtxSingleSingle,
3971 context: UnitContextProvider,
3972 }
3973 }
3974
3975 #[test]
3976 fn plugin_output_mutable_model_arm_works() {
3977 let mut input = vec![1, 2];
3978 let out = OutputMutableModelRunner::run_mutable_model(&mut input);
3979
3980 assert_eq!(out, 3);
3981 assert_eq!(input, vec![1, 2, 1]);
3982
3983 let mut input = vec![5, 4, 3, 2];
3984 let out = OutputMutableModelRunner::run_mutable_model(&mut input);
3985
3986 assert_eq!(out, 5);
3987 assert_eq!(input, vec![5, 4, 3, 2, 1]);
3988 }
3989
3990 // Variant 3: Immutable family-selected model arm
3991
3992 declare_family! {
3993 root: pub OutputFamilyRoot,
3994 child: [Run]
3995 }
3996
3997 define_family! {
3998 root: OutputFamilyRoot,
3999 family: OutputFamily,
4000 input: Input,
4001 output: Output,
4002 context: (),
4003 bounds: [
4004 Input: Into<u8>,
4005 Output: From<u8>,
4006 ],
4007 child: [
4008 Run => PureNoCtxSingleSingle,
4009 ],
4010 }
4011
4012 struct OutputFamilyRunner;
4013
4014 impl OutputFamilyRunner {
4015 plugin_output! {
4016 pub fn run_family_model,
4017 input: u8,
4018 output: u8,
4019 root: OutputFamilyRoot,
4020 family: OutputFamily,
4021 child: Run,
4022 context: UnitContextProvider,
4023 }
4024 }
4025
4026 #[test]
4027 fn plugin_output_immutable_family_arm_works() {
4028 assert_eq!(OutputFamilyRunner::run_family_model(10), 11);
4029 assert_eq!(OutputFamilyRunner::run_family_model(0), 1);
4030 }
4031
4032 // Variant 4: Mutable family-selected model arm
4033
4034 declare_family! {
4035 root: mut pub OutputMutFamilyRoot,
4036 child: [RunMut]
4037 }
4038
4039 define_family! {
4040 root: OutputMutFamilyRoot,
4041 family: OutputMutFamily,
4042 input: Input,
4043 output: Output,
4044 context: (),
4045 bounds: [
4046 Input: From<Vec<u8>> + Into<Vec<u8>> + Default,
4047 Output: From<usize>,
4048 ],
4049 child: [
4050 RunMut => MutNoCtxSingleSingle,
4051 ],
4052 }
4053
4054 struct OutputMutFamilyRunner;
4055
4056 impl OutputMutFamilyRunner {
4057 plugin_output! {
4058 pub fn run_mut_family_model,
4059 input: mut Vec<u8>,
4060 output: usize,
4061 root: OutputMutFamilyRoot,
4062 family: OutputMutFamily,
4063 child: RunMut,
4064 context: UnitContextProvider,
4065 }
4066 }
4067
4068 #[test]
4069 fn plugin_output_mutable_family_arm_works() {
4070 let mut input = vec![1, 2];
4071 let out = OutputMutFamilyRunner::run_mut_family_model(&mut input);
4072
4073 assert_eq!(out, 3);
4074 assert_eq!(input, vec![1, 2, 1]);
4075
4076 let mut input = vec![9];
4077 let out = OutputMutFamilyRunner::run_mut_family_model(&mut input);
4078
4079 assert_eq!(out, 2);
4080 assert_eq!(input, vec![9, 1]);
4081 }
4082
4083 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4084 // ``````````````````````````````` DECLARE FAMILY ````````````````````````````````
4085 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4086
4087 //--- Variant 1: Immutable Family ---
4088
4089 declare_family! {
4090 root: pub ImmutableDeclaredRoot,
4091 child: [DeclaredRun, DeclaredEcho]
4092 }
4093
4094 struct ImmutableDeclaredFamily;
4095
4096 impl ImmutableDeclaredRoot<u8, (), u8> for ImmutableDeclaredFamily {
4097 type DeclaredRun = PureNoCtxSingleSingle;
4098 type DeclaredEcho = PureNoCtxSingleSingle;
4099 }
4100
4101 fn run_declared_immutable_family(input: u8) -> u8 {
4102 let model =
4103 <ImmutableDeclaredFamily as ImmutableDeclaredRoot<u8, (), u8>>::DeclaredRun::default();
4104 let context = ();
4105
4106 <<ImmutableDeclaredFamily as ImmutableDeclaredRoot<u8, (), u8>>::DeclaredRun
4107 as PurePluginModel<u8, (), u8>>::compute(&model, input, &context)
4108 }
4109
4110 #[test]
4111 fn declare_family_immutable_arm_creates_root_and_children() {
4112 assert_eq!(run_declared_immutable_family(10), 11);
4113 assert_eq!(run_declared_immutable_family(0), 1);
4114 }
4115
4116 #[test]
4117 fn declare_family_immutable_arm_child_markers_exist() {
4118 let _run = DeclaredRun;
4119 let _echo = DeclaredEcho;
4120 }
4121
4122 //--- Variant 2: Mutable Family ---
4123
4124 declare_family! {
4125 root: mut pub MutableDeclaredRoot,
4126 child: [DeclaredRunMut, DeclaredNormalize]
4127 }
4128
4129 struct MutableDeclaredFamily;
4130
4131 impl MutableDeclaredRoot<Vec<u8>, (), usize> for MutableDeclaredFamily {
4132 type DeclaredRunMut = MutNoCtxSingleSingle;
4133 type DeclaredNormalize = MutNoCtxSingleSingle;
4134 }
4135
4136 fn run_declared_mutable_family(mut input: Vec<u8>) -> (Vec<u8>, usize) {
4137 let model =
4138 <MutableDeclaredFamily as MutableDeclaredRoot<Vec<u8>, (), usize>>::DeclaredRunMut::default();
4139 let context = ();
4140
4141 let out =
4142 <<MutableDeclaredFamily as MutableDeclaredRoot<Vec<u8>, (), usize>>::DeclaredRunMut
4143 as MutablePluginModel<Vec<u8>, (), usize>>::compute_mut(
4144 &model,
4145 &mut input,
4146 &context,
4147 );
4148
4149 (input, out)
4150 }
4151
4152 #[test]
4153 fn declare_family_mutable_arm_creates_root_and_children() {
4154 let (input, out) = run_declared_mutable_family(vec![1, 2]);
4155 assert_eq!(input, vec![1, 2, 1]);
4156 assert_eq!(out, 3);
4157 }
4158
4159 #[test]
4160 fn declare_family_mutable_arm_child_markers_exist() {
4161 let _run = DeclaredRunMut;
4162 let _normalize = DeclaredNormalize;
4163 }
4164
4165 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4166 // ```````````````````````````````` DEFINE FAMILY ````````````````````````````````
4167 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4168
4169 //--- With Context, Single Input, Single Output ---
4170
4171 declare_family! {
4172 root: pub DefFamCtxSingleSingleRoot,
4173 child: [DefFamCtxSingleSingleChild]
4174 }
4175
4176 define_family! {
4177 root: DefFamCtxSingleSingleRoot,
4178 family: DefFamCtxSingleSingleFamily,
4179 input: Input,
4180 output: Output,
4181 context: BasicConfig,
4182 bounds: [Input: Into<u8>, Output: From<u8>],
4183 child: [
4184 DefFamCtxSingleSingleChild => PureCtxSingleSingle,
4185 ],
4186 }
4187
4188 fn run_define_family_ctx_single_single(input: u8) -> u8 {
4189 let model = <DefFamCtxSingleSingleFamily as DefFamCtxSingleSingleRoot<
4190 u8,
4191 BasicConfig,
4192 u8,
4193 >>::DefFamCtxSingleSingleChild::default();
4194
4195 let ctx = BasicConfig { value: 10 };
4196
4197 <<DefFamCtxSingleSingleFamily as DefFamCtxSingleSingleRoot<u8, BasicConfig, u8>>
4198 ::DefFamCtxSingleSingleChild as PurePluginModel<u8, BasicConfig, u8>>
4199 ::compute(&model, input, &ctx)
4200 }
4201
4202 #[test]
4203 fn define_family_with_context_single_input_single_output_works() {
4204 assert_eq!(run_define_family_ctx_single_single(10), 20);
4205 assert_eq!(run_define_family_ctx_single_single(1), 11);
4206 }
4207
4208 //--- With Context, Single Input, Tuple Output ---
4209
4210 declare_family! {
4211 root: pub DefFamCtxSingleTupleRoot,
4212 child: [DefFamCtxSingleTupleChild]
4213 }
4214
4215 define_family! {
4216 root: DefFamCtxSingleTupleRoot,
4217 family: DefFamCtxSingleTupleFamily,
4218 input: Input,
4219 output: (OutA, OutB),
4220 context: BasicConfig,
4221 bounds: [Input: Into<u8>, OutA: From<u8>, OutB: From<u8>],
4222 child: [
4223 DefFamCtxSingleTupleChild => PureCtxSingleTuple,
4224 ],
4225 }
4226
4227 fn run_define_family_ctx_single_tuple(input: u8) -> (u8, u8) {
4228 let model = <DefFamCtxSingleTupleFamily as DefFamCtxSingleTupleRoot<
4229 u8,
4230 BasicConfig,
4231 (u8, u8),
4232 >>::DefFamCtxSingleTupleChild::default();
4233
4234 let ctx = BasicConfig { value: 1 };
4235
4236 <<DefFamCtxSingleTupleFamily as DefFamCtxSingleTupleRoot<u8, BasicConfig, (u8, u8)>>
4237 ::DefFamCtxSingleTupleChild as PurePluginModel<u8, BasicConfig, (u8, u8)>>
4238 ::compute(&model, input, &ctx)
4239 }
4240
4241 #[test]
4242 fn define_family_with_context_single_input_tuple_output_works() {
4243 assert_eq!(run_define_family_ctx_single_tuple(5), (5, 6));
4244 assert_eq!(run_define_family_ctx_single_tuple(1), (1, 2));
4245 }
4246
4247 // With Context, Tuple Input, Single Output
4248
4249 declare_family! {
4250 root: pub DefFamCtxTupleSingleRoot,
4251 child: [DefFamCtxTupleSingleChild]
4252 }
4253
4254 define_family! {
4255 root: DefFamCtxTupleSingleRoot,
4256 family: DefFamCtxTupleSingleFamily,
4257 input: (InpA, InpB),
4258 output: Output,
4259 context: BasicConfig,
4260 bounds: [InpA: Into<u8>, InpB: Into<u8>, Output: From<u8>],
4261 child: [
4262 DefFamCtxTupleSingleChild => PureCtxTupleSingle,
4263 ],
4264 }
4265
4266 fn run_define_family_ctx_tuple_single(input: (u8, u8)) -> u8 {
4267 let model = <DefFamCtxTupleSingleFamily as DefFamCtxTupleSingleRoot<
4268 (u8, u8),
4269 BasicConfig,
4270 u8,
4271 >>::DefFamCtxTupleSingleChild::default();
4272
4273 let ctx = BasicConfig { value: 10 };
4274
4275 <<DefFamCtxTupleSingleFamily as DefFamCtxTupleSingleRoot<(u8, u8), BasicConfig, u8>>
4276 ::DefFamCtxTupleSingleChild as PurePluginModel<(u8, u8), BasicConfig, u8>>
4277 ::compute(&model, input, &ctx)
4278 }
4279
4280 #[test]
4281 fn define_family_with_context_tuple_input_single_output_works() {
4282 assert_eq!(run_define_family_ctx_tuple_single((10, 10)), 30);
4283 assert_eq!(run_define_family_ctx_tuple_single((5, 30)), 45);
4284 }
4285
4286 //--- With Context, Tuple Input, Tuple Output ---
4287
4288 declare_family! {
4289 root: pub DefFamCtxTupleTupleRoot,
4290 child: [DefFamCtxTupleTupleChild]
4291 }
4292
4293 define_family! {
4294 root: DefFamCtxTupleTupleRoot,
4295 family: DefFamCtxTupleTupleFamily,
4296 input: (InpA, InpB),
4297 output: (OutA, OutB),
4298 context: BasicConfig,
4299 bounds: [InpA: Into<u8>, InpB: Into<u8>, OutA: From<u8>, OutB: From<u8>],
4300 child: [
4301 DefFamCtxTupleTupleChild => PureCtxTupleTuple,
4302 ],
4303 }
4304
4305 fn run_define_family_ctx_tuple_tuple(input: (u8, u8)) -> (u8, u8) {
4306 let model = <DefFamCtxTupleTupleFamily as DefFamCtxTupleTupleRoot<
4307 (u8, u8),
4308 BasicConfig,
4309 (u8, u8),
4310 >>::DefFamCtxTupleTupleChild::default();
4311
4312 let ctx = BasicConfig { value: 5 };
4313
4314 <<DefFamCtxTupleTupleFamily as DefFamCtxTupleTupleRoot<(u8, u8), BasicConfig, (u8, u8)>>
4315 ::DefFamCtxTupleTupleChild as PurePluginModel<(u8, u8), BasicConfig, (u8, u8)>>
4316 ::compute(&model, input, &ctx)
4317 }
4318
4319 #[test]
4320 fn define_family_with_context_tuple_input_tuple_output_works() {
4321 assert_eq!(run_define_family_ctx_tuple_tuple((5, 10)), (10, 15));
4322 assert_eq!(run_define_family_ctx_tuple_tuple((1, 0)), (6, 5));
4323 }
4324
4325 //--- No Context, Single Input, Single Output ---
4326
4327 declare_family! {
4328 root: pub DefFamNoCtxSingleSingleRoot,
4329 child: [DefFamNoCtxSingleSingleChild]
4330 }
4331
4332 define_family! {
4333 root: DefFamNoCtxSingleSingleRoot,
4334 family: DefFamNoCtxSingleSingleFamily,
4335 input: Input,
4336 output: Output,
4337 bounds: [Input: Into<u8>, Output: From<u8>],
4338 child: [
4339 DefFamNoCtxSingleSingleChild => PureNoCtxSingleSingle,
4340 ],
4341 }
4342
4343 fn run_define_family_no_ctx_single_single(input: u8) -> u8 {
4344 let model =
4345 <DefFamNoCtxSingleSingleFamily as DefFamNoCtxSingleSingleRoot<u8, (), u8>>
4346 ::DefFamNoCtxSingleSingleChild::default();
4347
4348 let ctx = ();
4349
4350 <<DefFamNoCtxSingleSingleFamily as DefFamNoCtxSingleSingleRoot<u8, (), u8>>
4351 ::DefFamNoCtxSingleSingleChild as PurePluginModel<u8, (), u8>>
4352 ::compute(&model, input, &ctx)
4353 }
4354
4355 #[test]
4356 fn define_family_no_context_single_input_single_output_works() {
4357 assert_eq!(run_define_family_no_ctx_single_single(10), 11);
4358 assert_eq!(run_define_family_no_ctx_single_single(0), 1);
4359 }
4360
4361 //--- No Context, Single Input, Tuple Output ---
4362
4363 declare_family! {
4364 root: pub DefFamNoCtxSingleTupleRoot,
4365 child: [DefFamNoCtxSingleTupleChild]
4366 }
4367
4368 define_family! {
4369 root: DefFamNoCtxSingleTupleRoot,
4370 family: DefFamNoCtxSingleTupleFamily,
4371 input: Input,
4372 output: (OutA, OutB),
4373 bounds: [Input: Into<u8>, OutA: From<u8>, OutB: From<u8>],
4374 child: [
4375 DefFamNoCtxSingleTupleChild => PureNoCtxSingleTuple,
4376 ],
4377 }
4378
4379 fn run_define_family_no_ctx_single_tuple(input: u8) -> (u8, u8) {
4380 let model = <DefFamNoCtxSingleTupleFamily as DefFamNoCtxSingleTupleRoot<
4381 u8,
4382 (),
4383 (u8, u8),
4384 >>::DefFamNoCtxSingleTupleChild::default();
4385
4386 let ctx = ();
4387
4388 <<DefFamNoCtxSingleTupleFamily as DefFamNoCtxSingleTupleRoot<u8, (), (u8, u8)>>
4389 ::DefFamNoCtxSingleTupleChild as PurePluginModel<u8, (), (u8, u8)>>
4390 ::compute(&model, input, &ctx)
4391 }
4392
4393 #[test]
4394 fn define_family_no_context_single_input_tuple_output_works() {
4395 assert_eq!(run_define_family_no_ctx_single_tuple(10), (10, 11));
4396 assert_eq!(run_define_family_no_ctx_single_tuple(0), (0, 1));
4397 }
4398
4399 //--- No Context, Tuple Input, Single Output ---
4400
4401 declare_family! {
4402 root: pub DefFamNoCtxTupleSingleRoot,
4403 child: [DefFamNoCtxTupleSingleChild]
4404 }
4405
4406 define_family! {
4407 root: DefFamNoCtxTupleSingleRoot,
4408 family: DefFamNoCtxTupleSingleFamily,
4409 input: (InpA, InpB),
4410 output: Output,
4411 bounds: [InpA: Into<u8>, InpB: Into<u8>, Output: From<u8>],
4412 child: [
4413 DefFamNoCtxTupleSingleChild => PureNoCtxTupleSingle,
4414 ],
4415 }
4416
4417 fn run_define_family_no_ctx_tuple_single(input: (u8, u8)) -> u8 {
4418 let model = <DefFamNoCtxTupleSingleFamily as DefFamNoCtxTupleSingleRoot<
4419 (u8, u8),
4420 (),
4421 u8,
4422 >>::DefFamNoCtxTupleSingleChild::default();
4423
4424 let ctx = ();
4425
4426 <<DefFamNoCtxTupleSingleFamily as DefFamNoCtxTupleSingleRoot<(u8, u8), (), u8>>
4427 ::DefFamNoCtxTupleSingleChild as PurePluginModel<(u8, u8), (), u8>>
4428 ::compute(&model, input, &ctx)
4429 }
4430
4431 #[test]
4432 fn define_family_no_context_tuple_input_single_output_works() {
4433 assert_eq!(run_define_family_no_ctx_tuple_single((10, 11)), 21);
4434 assert_eq!(run_define_family_no_ctx_tuple_single((0, 5)), 5);
4435 }
4436
4437 //--- No Context, Tuple Input, Tuple Output ---
4438
4439 declare_family! {
4440 root: pub DefFamNoCtxTupleTupleRoot,
4441 child: [DefFamNoCtxTupleTupleChild]
4442 }
4443
4444 define_family! {
4445 root: DefFamNoCtxTupleTupleRoot,
4446 family: DefFamNoCtxTupleTupleFamily,
4447 input: (InpA, InpB),
4448 output: (OutA, OutB),
4449 bounds: [InpA: Into<u8>, InpB: Into<u8>, OutA: From<u8>, OutB: From<u8>],
4450 child: [
4451 DefFamNoCtxTupleTupleChild => PureNoCtxTupleTuple,
4452 ],
4453 }
4454
4455 fn run_define_family_no_ctx_tuple_tuple(input: (u8, u8)) -> (u8, u8) {
4456 let model = <DefFamNoCtxTupleTupleFamily as DefFamNoCtxTupleTupleRoot<
4457 (u8, u8),
4458 (),
4459 (u8, u8),
4460 >>::DefFamNoCtxTupleTupleChild::default();
4461
4462 let ctx = ();
4463
4464 <<DefFamNoCtxTupleTupleFamily as DefFamNoCtxTupleTupleRoot<(u8, u8), (), (u8, u8)>>
4465 ::DefFamNoCtxTupleTupleChild as PurePluginModel<(u8, u8), (), (u8, u8)>>
4466 ::compute(&model, input, &ctx)
4467 }
4468
4469 #[test]
4470 fn define_family_no_context_tuple_input_tuple_output_works() {
4471 assert_eq!(run_define_family_no_ctx_tuple_tuple((5, 10)), (50, 100));
4472 assert_eq!(run_define_family_no_ctx_tuple_tuple((1, 0)), (10, 0));
4473 }
4474
4475 //--- With Context + marker ---
4476
4477 plugin_model! {
4478 name: pub PureGenericCtxSingleSingle,
4479 input: Input,
4480 output: Output,
4481 others: [T],
4482 context: GenericConfig<T>,
4483 bounds: [Input: Into<u8>, Output: From<u8>, T: Into<u8> + Clone],
4484 compute: |input, ctx| {
4485 Output::from(input.into() + ctx.value.clone().into())
4486 },
4487 }
4488
4489 declare_family! {
4490 root: pub DefFamCtxMarkerRoot,
4491 child: [DefFamCtxMarkerChild]
4492 }
4493
4494 define_family! {
4495 root: DefFamCtxMarkerRoot,
4496 family: DefFamCtxMarkerFamily,
4497 input: Input,
4498 output: Output,
4499 context: GenericConfig<T>,
4500 marker: [T],
4501 bounds: [Input: Into<u8>, Output: From<u8>, T: Into<u8> + Clone],
4502 child: [
4503 DefFamCtxMarkerChild => PureGenericCtxSingleSingle,
4504 ],
4505 }
4506
4507 fn run_define_family_ctx_marker(input: u8) -> u8 {
4508 let model =
4509 <DefFamCtxMarkerFamily as DefFamCtxMarkerRoot<u8, GenericConfig<u8>, u8>>
4510 ::DefFamCtxMarkerChild::default();
4511
4512 let ctx = GenericConfig { value: 7u8 };
4513
4514 <<DefFamCtxMarkerFamily as DefFamCtxMarkerRoot<u8, GenericConfig<u8>, u8>>
4515 ::DefFamCtxMarkerChild as PurePluginModel<u8, GenericConfig<u8>, u8>>
4516 ::compute(&model, input, &ctx)
4517 }
4518
4519 #[test]
4520 fn define_family_with_context_marker_works() {
4521 assert_eq!(run_define_family_ctx_marker(10), 17);
4522 assert_eq!(run_define_family_ctx_marker(0), 7);
4523 }
4524
4525 //--- With Context + borrow + marker ---
4526
4527 plugin_model! {
4528 name: pub PureGenericBorrowCtx,
4529 input: Input,
4530 output: Output,
4531 others: [T],
4532 context: GenericConfig<T>,
4533 bounds: [Input: AsRef<[u8]>, Output: From<usize>, T: Clone],
4534 compute: |input, _ctx| {
4535 Output::from(input.as_ref().len())
4536 },
4537 }
4538
4539 declare_family! {
4540 root: pub DefFamCtxBorrowMarkerRoot,
4541 child: [DefFamCtxBorrowMarkerChild]
4542 }
4543
4544 define_family! {
4545 root: DefFamCtxBorrowMarkerRoot,
4546 family: DefFamCtxBorrowMarkerFamily,
4547 borrow: ['a],
4548 input: Input,
4549 output: Output,
4550 context: GenericConfig<T>,
4551 marker: [T],
4552 bounds: [Input: AsRef<[u8]> + 'a, Output: From<usize>, T: Clone],
4553 child: [
4554 DefFamCtxBorrowMarkerChild => PureGenericBorrowCtx,
4555 ],
4556 }
4557
4558 fn run_define_family_ctx_borrow_marker<'a>(input: &'a [u8]) -> usize {
4559 let model = <DefFamCtxBorrowMarkerFamily<'a> as DefFamCtxBorrowMarkerRoot<
4560 &'a [u8],
4561 GenericConfig<u8>,
4562 usize,
4563 >>::DefFamCtxBorrowMarkerChild::default();
4564
4565 let ctx = GenericConfig { value: 99u8 };
4566
4567 <<DefFamCtxBorrowMarkerFamily<'a> as DefFamCtxBorrowMarkerRoot<&'a [u8], GenericConfig<u8>, usize>>
4568 ::DefFamCtxBorrowMarkerChild as PurePluginModel<&'a [u8], GenericConfig<u8>, usize>>
4569 ::compute(&model, input, &ctx)
4570 }
4571
4572 #[test]
4573 fn define_family_with_context_borrow_and_marker_works() {
4574 let data = [1u8, 2, 3, 4];
4575 assert_eq!(run_define_family_ctx_borrow_marker(&data), 4);
4576
4577 let data = [9u8];
4578 assert_eq!(run_define_family_ctx_borrow_marker(&data), 1);
4579 }
4580
4581 //--- No Context + multiple children ---
4582
4583 declare_family! {
4584 root: pub DefFamNoCtxMultiChildRoot,
4585 child: [DefFamNoCtxMultiChildA, DefFamNoCtxMultiChildB]
4586 }
4587
4588 define_family! {
4589 root: DefFamNoCtxMultiChildRoot,
4590 family: DefFamNoCtxMultiChildFamily,
4591 input: Input,
4592 output: Output,
4593 bounds: [Input: Into<u8>, Output: From<u8>],
4594 child: [
4595 DefFamNoCtxMultiChildA => PureNoCtxSingleSingle,
4596 DefFamNoCtxMultiChildB => PureNoCtxSingleSingle,
4597 ],
4598 }
4599
4600 fn run_define_family_no_ctx_multi_child_a(input: u8) -> u8 {
4601 let model =
4602 <DefFamNoCtxMultiChildFamily as DefFamNoCtxMultiChildRoot<u8, (), u8>>
4603 ::DefFamNoCtxMultiChildA::default();
4604
4605 let ctx = ();
4606
4607 <<DefFamNoCtxMultiChildFamily as DefFamNoCtxMultiChildRoot<u8, (), u8>>
4608 ::DefFamNoCtxMultiChildA as PurePluginModel<u8, (), u8>>
4609 ::compute(&model, input, &ctx)
4610 }
4611
4612 fn run_define_family_no_ctx_multi_child_b(input: u8) -> u8 {
4613 let model =
4614 <DefFamNoCtxMultiChildFamily as DefFamNoCtxMultiChildRoot<u8, (), u8>>
4615 ::DefFamNoCtxMultiChildB::default();
4616
4617 let ctx = ();
4618
4619 <<DefFamNoCtxMultiChildFamily as DefFamNoCtxMultiChildRoot<u8, (), u8>>
4620 ::DefFamNoCtxMultiChildB as PurePluginModel<u8, (), u8>>
4621 ::compute(&model, input, &ctx)
4622 }
4623
4624 #[test]
4625 fn define_family_multiple_children_work() {
4626 assert_eq!(run_define_family_no_ctx_multi_child_a(10), 11);
4627 assert_eq!(run_define_family_no_ctx_multi_child_b(0), 1);
4628
4629 let _a = DefFamNoCtxMultiChildA;
4630 let _b = DefFamNoCtxMultiChildB;
4631 }
4632
4633 //--- No Context + multiple children with distinct models ---
4634
4635 plugin_model! {
4636 name: pub PureNoCtxSingleSingleDouble,
4637 input: Input,
4638 output: Output,
4639 bounds: [Input: Into<u8>, Output: From<u8>],
4640 compute: |input, _ctx| {
4641 let x = input.into();
4642 Output::from(x * 2)
4643 }
4644 }
4645
4646 declare_family! {
4647 root: pub DefFamNoCtxMultiChildDistinctRoot,
4648 child: [DefFamNoCtxMultiChildInc, DefFamNoCtxMultiChildDouble]
4649 }
4650
4651 define_family! {
4652 root: DefFamNoCtxMultiChildDistinctRoot,
4653 family: DefFamNoCtxMultiChildDistinctFamily,
4654 input: Input,
4655 output: Output,
4656 bounds: [Input: Into<u8>, Output: From<u8>],
4657 child: [
4658 DefFamNoCtxMultiChildInc => PureNoCtxSingleSingle,
4659 DefFamNoCtxMultiChildDouble => PureNoCtxSingleSingleDouble,
4660 ],
4661 }
4662
4663 fn run_define_family_no_ctx_multi_child_inc(input: u8) -> u8 {
4664 let model = <DefFamNoCtxMultiChildDistinctFamily as DefFamNoCtxMultiChildDistinctRoot<
4665 u8,
4666 (),
4667 u8,
4668 >>::DefFamNoCtxMultiChildInc::default();
4669
4670 let ctx = ();
4671
4672 <<DefFamNoCtxMultiChildDistinctFamily as DefFamNoCtxMultiChildDistinctRoot<u8, (), u8>>
4673 ::DefFamNoCtxMultiChildInc as PurePluginModel<u8, (), u8>>
4674 ::compute(&model, input, &ctx)
4675 }
4676
4677 fn run_define_family_no_ctx_multi_child_double(input: u8) -> u8 {
4678 let model = <DefFamNoCtxMultiChildDistinctFamily as DefFamNoCtxMultiChildDistinctRoot<
4679 u8,
4680 (),
4681 u8,
4682 >>::DefFamNoCtxMultiChildDouble::default();
4683
4684 let ctx = ();
4685
4686 <<DefFamNoCtxMultiChildDistinctFamily as DefFamNoCtxMultiChildDistinctRoot<u8, (), u8>>
4687 ::DefFamNoCtxMultiChildDouble as PurePluginModel<u8, (), u8>>
4688 ::compute(&model, input, &ctx)
4689 }
4690
4691 #[test]
4692 fn define_family_multiple_children_with_distinct_models_work() {
4693 assert_eq!(run_define_family_no_ctx_multi_child_inc(10), 11);
4694 assert_eq!(run_define_family_no_ctx_multi_child_inc(0), 1);
4695
4696 assert_eq!(run_define_family_no_ctx_multi_child_double(10), 20);
4697 assert_eq!(run_define_family_no_ctx_multi_child_double(3), 6);
4698
4699 let _inc = DefFamNoCtxMultiChildInc;
4700 let _double = DefFamNoCtxMultiChildDouble;
4701 }
4702
4703 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4704 // ```````````````````````````````` HELPER MACROS ````````````````````````````````
4705 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4706
4707 #[test]
4708 fn phantom_struct_no_lifetime_no_generic_compiles() {
4709 __phantom_struct!(pub Plain [] []);
4710 let _x = Plain;
4711 }
4712
4713 #[test]
4714 fn phantom_struct_lifetime_only_compiles() {
4715 __phantom_struct!(pub WithLt ['a] []);
4716 let _x: WithLt<'static>;
4717 }
4718
4719 #[test]
4720 fn phantom_struct_generic_only_compiles() {
4721 __phantom_struct!(pub WithGen [] [T]);
4722 let _x: WithGen<u8>;
4723 }
4724
4725 #[test]
4726 fn phantom_struct_lifetime_and_generic_compiles() {
4727 __phantom_struct!(pub WithLtGen ['a] [T]);
4728 let _x: WithLtGen<'static, u8>;
4729 }
4730
4731 #[test]
4732 fn phantom_struct_generic_is_covariant() {
4733 __phantom_struct!(pub CovGen [] [T]);
4734 // covariance: Foo<&'static str> can be used where Foo<&'short str> is expected
4735 fn accepts<'a>(_: CovGen<&'a str>) {}
4736 let x: CovGen<&'static str> = CovGen(PhantomData);
4737 accepts(x);
4738 }
4739
4740 #[test]
4741 fn phantom_struct_lifetime_and_generic_is_covariant() {
4742 __phantom_struct!(pub CovLtGen ['a] [T]);
4743 fn accepts<'a>(_: CovLtGen<'a, &'a str>) {}
4744 let x: CovLtGen<'static, &'static str> = CovLtGen(PhantomData);
4745 accepts(x);
4746 }
4747
4748 #[test]
4749 fn phantom_struct_field_types_are_correct() {
4750 // Arm 2: PhantomData<(&'a (),)>
4751 __phantom_struct!(pub LtField ['a] []);
4752 let _: LtField<'static> = LtField(PhantomData::<(&'static (),)>);
4753
4754 // Arm 3: PhantomData<(T,)>
4755 __phantom_struct!(pub GenField [] [T]);
4756 let _: GenField<u8> = GenField(PhantomData::<(u8,)>);
4757
4758 // Arm 4: PhantomData<(T, &'a ())> generics first, then lifetime references
4759 __phantom_struct!(pub LtGenField ['a] [T]);
4760 let _: LtGenField<'static, u8> = LtGenField(PhantomData::<(u8, &'static ())>);
4761 }
4762}