### Setup for calling a visitor Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Example setup code for defining structures and the visitor macro before calling a visitor. ```rust # use despatma::visitor; # # visitor!( # Arc, # Rectangle, # Point, # ); # # struct Arc { # center: Point, # radius: u32, # } # # struct Rectangle { # top_right: Point, # bottom_left: Point, # } # # impl Rectangle { # fn new(left: i32, bottom: i32, right: i32, top: i32) -> Self { # Self { # top_right: Point { x: right, y: top }, # bottom_left: Point { x: left, y: bottom }, # } # } # } # # # struct Point { # x: i32, # y: i32, # } ``` -------------------------------- ### Example Input for abstract_factory Source: https://docs.rs/despatma/latest/despatma/attr.abstract_factory.html An example showing the definition of a factory method and the application of the abstract_factory macro to a trait. ```rust use despatma::abstract_factory; use std::fmt::Display; // A factory method // `Element` is a trait defined by you. pub trait Factory { fn create(&self, name: String) -> Box; } // The abstract factory that also needs to implement `Display` and `Eq`. // The factory method above (`Factory`) is the first input. #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} pub trait Element { fn create() -> Self where Self: Sized; } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } ``` -------------------------------- ### Example input for visitor! macro Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Defines the visitor! macro invocation with a list of types (Arc, Rectangle, Point) and the struct definitions for those types. This is the input that generates the visitor pattern code. ```rust use despatma::visitor; visitor!( Arc, Rectangle, Point, ); struct Arc { center: Point, radius: u32, } struct Rectangle { top_right: Point, bottom_left: Point, } struct Point { x: i32, y: i32, } ``` -------------------------------- ### Example input using #[interpolate_traits] Source: https://docs.rs/despatma/latest/despatma/attr.interpolate_traits.html Shows the input code for the `#[interpolate_traits]` macro. It defines a `Factory` trait and uses `#[abstract_factory]` to create a `GuiFactory` trait. The macro is applied to two `impl` blocks: one for `Factory` and one for `TRAIT` (Element). The `TRAIT` and `CONCRETE` markers are replaced with each element from the list. ```rust use despatma::{abstract_factory, interpolate_traits}; use std::fmt::Display; // GuiFactory and Factory are explained in the abstract_factory example struct GnomeFactory{} pub trait Factory { fn create(&self, name: String) -> Box; } #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} // Implement the factory method for each GUI element #[interpolate_traits( Button => GnomeButton, Scroller => Gnome2Scroller, Window => Gnome3Window, )] impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(CONCRETE::create(name)) } } pub trait Element { fn create(name: String) -> Self where Self: Sized; } #[interpolate_traits( Element => GnomeButton, Element => Gnome2Scroller, Element => Gnome3Window, )] impl TRAIT for CONCRETE { fn create(name: String) -> Self { CONCRETE { name } } } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } struct GnomeButton { name: String, } impl Button for GnomeButton { fn click(&self) {} } struct Gnome2Scroller { name: String, } impl Scroller for Gnome2Scroller { fn scroll(&self, _x: i32, _y: i32) {} } struct Gnome3Window { name: String, } impl Window for Gnome3Window { fn resize(&self, _width: u32, _height: u32) {} } ``` -------------------------------- ### visitor! macro with helper_tmpl option Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Shows how to use the helper_tmpl option to fill the body of the helper functions with custom code. The example provides traversal code for Arc and Rectangle types. ```rust use despatma::visitor; visitor!( #[helper_tmpl = {visitor.visit_point(&arc.center);} ] Arc, #[ helper_tmpl = { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); }, ] Rectangle, Point, ); ``` -------------------------------- ### Singleton and scoped dependencies with #[dependency_container] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Construct a container and register dependencies with `#[Singleton]`, `#[Scoped]`, or `#[Transient]` attributes. The singleton `Config` is created once, while `Service` is re-created on each request. New scopes are started via `new_scope()`. The `service` method must take a reference to `Config` to avoid multiple owned instances. ```rust use despatma::dependency_container; // Implementation details... #[dependency_container] impl MyContainer { #[Singleton] async fn config(&self) -> Config { Config::new().await } fn service(&self, config: &Config) -> Service { Service::new(config.port) } } let container = MyContainer::new(); let service = container.service(); let new_container_scope = container.new_scope(); let service2 = container.service(); ``` -------------------------------- ### interpolate_traits macro output example Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html This shows the expanded code generated by the interpolate_traits macro from the previous input example. ```rust use despatma::{abstract_factory, interpolate_traits}; use std::fmt::Display; // GuiFactory and Factory are explained in the abstract_factory example struct GnomeFactory{} pub trait Factory { fn create(&self, name: String) -> Box; } #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} // Implement the factory method for each GUI element impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(GnomeButton::create(name)) } } impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(Gnome2Scroller::create(name)) } } impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(Gnome3Window::create(name)) } } pub trait Element { fn create(name: String) -> Self where Self: Sized; } impl Element for GnomeButton { fn create(name: String) -> Self { GnomeButton { name } } } impl Element for Gnome2Scroller { fn create(name: String) -> Self { Gnome2Scroller { name } } } impl Element for Gnome3Window { fn create(name: String) -> Self { Gnome3Window { name } } } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } struct GnomeButton { name: String, } impl Button for GnomeButton { fn click(&self) {} } struct Gnome2Scroller { name: String, } impl Scroller for Gnome2Scroller { fn scroll(&self, _x: i32, _y: i32) {} ``` -------------------------------- ### #[dependency_container] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html The dependency_container macro simplifies dependency injection in Rust by automatically wiring dependencies based on an impl block. It creates a dependency container with public methods that handle the correct setup and wiring of dependencies. ```APIDOC ## #[dependency_container] ### Description The `dependency_container` macro simplifies dependency injection in Rust by automatically wiring dependencies based on an `impl` block. It creates a dependency container with public methods that handle the correct setup and wiring of dependencies. ### Usage Apply the macro to an `impl` block. The macro creates a struct with the same name as the `impl` block, generates a `new()` method to instantiate the container, and generates public methods for each method in the `impl` block. Dependencies are auto-wired based on parameter names matching method names. ### Parameters - **visibility** (optional) - A Rust visibility modifier (e.g., `pub`, `pub(crate)`, `pub(super)`, `pub(in path)`) to control the visibility of the generated container struct. Default is private. ### Example ```rust use despatma::dependency_container; struct Config { port: u32, } struct Service; impl Service { pub fn new(port: u32) -> Self { Self } } #[dependency_container] impl MyContainer { fn config(&self) -> Config { Config { port: 8080 } } fn service(&self, config: Config) -> Service { Service::new(config.port) } } let container = MyContainer::new(); let service = container.service(); ``` ### Notes - The linking between dependencies works because the `config()` method has the same name as the `config` argument in the `service` method. This name matching is crucial for the auto-wiring to function correctly. - By default, the generated container struct is private. To make it public, add a visibility modifier to the macro invocation. - The macro supports all Rust visibility modifiers including `pub`, `pub(crate)`, `pub(super)`, and `pub(in path)`. ### Advanced Features #### Returning Traits The macro supports returning trait objects. This decouples the service from the concrete implementation and allows runtime swapping. ```rust use despatma::dependency_container; trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... #[dependency_container] impl Dependencies { #[Transient(Sqlite)] fn data_layer(&self) -> impl DataLayer { Sqlite } fn service(&self, data_layer: impl DataLayer) -> Service { Service::new(data_layer) } } ``` #### Runtime Abstractions For runtime dependency switching, use `Box` with `#[auto_impl(Box)]`. ```rust use auto_impl::auto_impl; use despatma::dependency_container; #[auto_impl(Box)] trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... #[dependency_container] impl DependencyContainer { #[Transient(Box)] fn data_layer(&self, config: Config) -> impl DataLayer { let dl: Box = if config.use_sqlite { Box::new(Sqlite) } else { Box::new(Postgres) }; dl } // Other methods... } ``` **Important**: To make this work: 1. Annotate the `DataLayer` trait with `#[auto_impl(Box)]`. 2. Use `impl DataLayer` as the return type, but create a `Box` internally. #### Async Dependencies The macro supports async dependencies by automatically making parent dependencies async. ```rust use despatma::dependency_container; // Implementation details... #[dependency_container] impl MyContainer { async fn config(&self) -> Config { Config::new().await } fn service(&self, config: Config) -> Service { Service::new(config.port) } } ``` Note that the `service` method will be automatically made `async` by the macro to accommodate the async `config` dependency. ``` -------------------------------- ### Usage example: PointCounter visitor Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Shows a custom visitor that implements the Visitor trait and overrides only the visit_point method to count points. It calls the helper function visit_point to keep traversal intact. ```rust struct PointCounter { pub count: usize, } impl Visitor for PointCounter { // Only override the visit_point method // All other methods will call the default helper function since we don't care about those type. // But we still need to call the helper function to keep the traversal intact. fn visit_point(&mut self, point: &Point) { self.count += 1; // Call helper function to keep traversal intact visit_point(self, point) } } ``` -------------------------------- ### interpolate_traits macro input example Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html This example demonstrates how to use the interpolate_traits attribute to expand a template over a list of trait and concrete type pairs. ```rust use despatma::{abstract_factory, interpolate_traits}; use std::fmt::Display; // GuiFactory and Factory are explained in the abstract_factory example struct GnomeFactory{} pub trait Factory { fn create(&self, name: String) -> Box; } #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} // Implement the factory method for each GUI element #[interpolate_traits( Button => GnomeButton, Scroller => Gnome2Scroller, Window => Gnome3Window, )] impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(CONCRETE::create(name)) } } pub trait Element { fn create(name: String) -> Self where Self: Sized; } #[interpolate_traits( Element => GnomeButton, Element => Gnome2Scroller, Element => Gnome3Window, )] impl TRAIT for CONCRETE { fn create(name: String) -> Self { CONCRETE { name } } } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } struct GnomeButton { name: String, } impl Button for GnomeButton { fn click(&self) {} } struct Gnome2Scroller { name: String, } impl Scroller for Gnome2Scroller { fn scroll(&self, _x: i32, _y: i32) {} } struct Gnome3Window { name: String, } impl Window for Gnome3Window { fn resize(&self, _width: u32, _height: u32) {} } ``` -------------------------------- ### visitor! macro with no_default option Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Shows how to use the no_default option to remove the default trait implementation for a specific type. In this example, the visit_arc method will have no default implementation, forcing visitors to implement it. ```rust use despatma::visitor; visitor!( #[ helper_tmpl = {visitor.visit_point(&arc.center);}, no_default, ] Arc, #[ helper_tmpl = { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); }, ] Rectangle, Point, ); ``` -------------------------------- ### Basic usage of #[dependency_container] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Shows the basic usage of the `dependency_container` macro. The macro creates a `MyContainer` struct with a `new()` method and public `config` and `service` methods. The `service` method is automatically wired to use the `config` method's output because the `config()` method has the same name as the `config` argument in the `service` method. ```rust use despatma::dependency_container; struct Config { port: u32, } struct Service; impl Service { pub fn new(port: u32) -> Self { Self } } #[dependency_container] impl MyContainer { fn config(&self) -> Config { Config { port: 8080 } } fn service(&self, config: Config) -> Service { Service::new(config.port) } } let container = MyContainer::new(); let service = container.service(); ``` -------------------------------- ### Async dependencies with #[dependency_container] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Shows how the macro supports async dependencies. The `config` method is `async`, and the `service` method will be automatically made `async` by the macro to accommodate the async `config` dependency. ```rust use despatma::dependency_container; // Implementation details... #[dependency_container] impl MyContainer { async fn config(&self) -> Config { Config::new().await } fn service(&self, config: Config) -> Service { Service::new(config.port) } } ``` -------------------------------- ### Invoking the Visitor Pattern Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Demonstrates three ways to invoke a visitor on a hierarchy. The first method is preferred as it is generic and does not require knowing the specific visit method name. ```rust /// # struct PointCounter { /// # pub count: usize, /// # } /// # /// # impl Visitor for PointCounter { /// # fn visit_point(&mut self, point: &Point) { /// # self.count += 1; /// # /// # // Call helper function to keep traversal intact /// # visit_point(self, point) /// # } /// # } /// // Create a rectangle with bottom-left point (0, 0) and top-right point (10, 12) /// let rect = Rectangle::new(0, 0, 10, 12); /// let mut point_stats = PointCounter{ count: 0 }; /// /// // Invoke visitor on hierarchy options /// rect.apply(&mut point_stats); // 1 - Preferred /// visit_rectangle(&mut point_stats, &rect); // 2 /// point_stats.visit_rectangle(&rect); // 3 ``` -------------------------------- ### Implementing a Custom Visitor Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Demonstrates how to implement the generated Visitor trait to perform custom logic, such as counting points in a hierarchy. ```rust # use despatma::visitor; # # visitor!( # Arc, # Rectangle, # Point, # ); # # struct Arc { # center: Point, # radius: u32, # } # # struct Rectangle { # top_right: Point, # bottom_left: Point, # } # # struct Point { # x: i32, # y: i32, # } # struct PointCounter { pub count: usize, } impl Visitor for PointCounter { // Only override the visit_point method // All other methods will call the default helper function since we don't care about those type. // But we still need to call the helper function to keep the traversal intact. fn visit_point(&mut self, point: &Point) { self.count += 1; // Call helper function to keep traversal intact visit_point(self, point) } } ``` -------------------------------- ### Container constructor with arguments Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Defines a static `new` method to initialize dependencies outside the container. Argument names must match the usage locations. Creates a container with `DependencyContainer::new(config)`. ```rust use despatma::dependency_container; struct Config { port: u32, } struct Service; impl Service { fn new(port: u32) -> Self { println!("Service started on port {}", port); Self } } #[dependency_container] impl DependencyContainer { fn new(config: Config) {} fn service(&self, config: &Config) -> Service { Service::new(config.port) } } let config = Config { port: 8080 }; let container = DependencyContainer::new(config); let _service = container.service(); ``` -------------------------------- ### Runtime abstractions with Box and #[auto_impl(Box)] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Shows how to use `Box` for runtime dependency switching. Requires annotating the trait with `#[auto_impl(Box)]` and using `impl DataLayer` as the return type while creating a `Box` internally to handle different concrete types. ```rust use auto_impl::auto_impl; use despatma::dependency_container; #[auto_impl(Box)] trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... #[dependency_container] impl DependencyContainer { #[Transient(Box)] fn data_layer(&self, config: Config) -> impl DataLayer { let dl: Box = if config.use_sqlite { Box::new(Sqlite) } else { Box::new(Postgres) }; dl } // Other methods... } ``` -------------------------------- ### Using the helper_tmpl Option Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html The helper_tmpl option allows defining custom code to fill the body of generated helper methods. ```rust use despatma::visitor; # # struct Arc { # center: Point, # radius: u32, # } # # struct Rectangle { # top_right: Point, # bottom_left: Point, # } # # struct Point { # x: i32, # y: i32, # } ``` -------------------------------- ### Define visitor with helper_tmpl Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Use the helper_tmpl attribute within the visitor! macro to define custom logic for visiting specific types. ```rust visitor!( #[helper_tmpl = {visitor.visit_point(&arc.center);} ] Arc, #[ helper_tmpl = { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); }, ] Rectangle, Point, ); ``` -------------------------------- ### Singleton with trait object and type hint (#[Singleton(Sqlite)]) Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Stores a `DataLayer` impl as a singleton while returning it as `impl DataLayer`. The `#[auto_impl(&)]` on the trait enables passing references. The attribute must be hinted with the concrete type `Sqlite` so the container knows what to store. ```rust use auto_impl::auto_impl; use despatma::dependency_container; #[auto_impl(&)] trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... #[dependency_container] impl Dependencies { #[Singleton(Sqlite)] fn data_layer(&self) -> impl DataLayer { Sqlite } fn service(&self, data_layer: impl DataLayer) -> Service { Service::new(data_layer) } } ``` -------------------------------- ### Generated output from visitor! macro Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Shows the generated code from the visitor! macro: a Visitor trait with default methods, helper functions (visit_arc, visit_rectangle, visit_point), and Visitable implementations for each type. The struct definitions are also repeated. ```rust use despatma::visitor; pub trait Visitor { fn visit_arc(&mut self, arc: &Arc) { visit_arc(self, arc) } fn visit_rectangle(&mut self, rectangle: &Rectangle) { visit_rectangle(self, rectangle) } fn visit_point(&mut self, point: &Point) { visit_point(self, point) } } pub fn visit_arc(visitor: &mut V, _arc: &Arc) where V: Visitor + ?Sized, { } pub fn visit_rectangle(visitor: &mut V, _rectangle: &Rectangle) where V: Visitor + ?Sized, { } pub fn visit_point(_visitor: &mut V, _point: &Point) where V: Visitor + ?Sized, { } trait Visitable { fn apply(&self, visitor: &mut impl Visitor); } impl Visitable for Arc { fn apply(&self, visitor: &mut impl Visitor) { visitor.visit_arc(self); } } impl Visitable for Rectangle { fn apply(&self, visitor: &mut impl Visitor) { visitor.visit_rectangle(self); } } impl Visitable for Point { fn apply(&self, visitor: &mut impl Visitor) { visitor.visit_point(self); } } struct Arc { center: Point, radius: u32, } struct Rectangle { top_right: Point, bottom_left: Point, } struct Point { x: i32, y: i32, } ``` -------------------------------- ### Singleton and Scoped Dependencies Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Use #[Singleton] or #[Scoped] attributes to manage dependency lifecycles. Singletons are constructed only once upon first request. ```rust use despatma::dependency_container; # use std::time::Duration; # use tokio::time::sleep; // Implementation details... # struct Config { # port: u32, # } # # impl Config { # async fn new() -> Self { # sleep(Duration::from_secs(1)).await; # Config { port: 8080 } # } # } # # struct Service; # # impl Service { # pub fn new(port: u32) -> Self { # Service # } # } #[dependency_container] impl MyContainer { #[Singleton] async fn config(&self) -> Config { Config::new().await } fn service(&self, config: &Config) -> Service { Service::new(config.port) } } let container = MyContainer::new(); let service = container.service(); let new_container_scope = container.new_scope(); let service2 = container.service(); ``` -------------------------------- ### Generated helper functions with helper_tmpl Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Shows the resulting helper functions after applying the helper_tmpl option. The visit_arc and visit_rectangle functions now contain the custom traversal code, while visit_point remains empty. ```rust // `Visitor` is same as earlier pub fn visit_arc(visitor: &mut V, arc: &Arc) where V: Visitor + ?Sized, { visitor.visit_point(&arc.center); } pub fn visit_rectangle(visitor: &mut V, rectangle: &Rectangle) where V: Visitor + ?Sized, { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); } pub fn visit_point(_visitor: &mut V, _point: &Point) where V: Visitor + ?Sized, { } // `Visitable` is same as earlier ``` -------------------------------- ### Returning Trait Objects in dependency_container Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Shows how to use the dependency_container macro with trait objects to decouple services from concrete implementations, facilitating easier testing and runtime swapping. ```rust use despatma::dependency_container; trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... # struct Sqlite; # # impl DataLayer for Sqlite { # fn get_user_name(&self, id: u32) -> String { # format!("User {}", id) # } # } # # struct Service { # data_layer: D, # } # # impl Service { # pub fn new(data_layer: D) -> Self { # Self { data_layer } # } # } #[dependency_container] impl Dependencies { #[Transient(Sqlite)] fn data_layer(&self) -> impl DataLayer { Sqlite } fn service(&self, data_layer: impl DataLayer) -> Service { Service::new(data_layer) } ``` -------------------------------- ### Generated helper functions Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html The resulting helper functions generated by the visitor! macro based on the provided templates. ```rust // `Visitor` is same as earlier # pub trait Visitor { # fn visit_arc(&mut self, arc: &Arc) { # visit_arc(self, arc) # } # fn visit_rectangle(&mut self, rectangle: &Rectangle) { # visit_rectangle(self, rectangle) # } # fn visit_point(&mut self, point: &Point) { # visit_point(self, point) # } # } pub fn visit_arc(visitor: &mut V, arc: &Arc) where V: Visitor + ?Sized, { visitor.visit_point(&arc.center); } pub fn visit_rectangle(visitor: &mut V, rectangle: &Rectangle) where V: Visitor + ?Sized, { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); } pub fn visit_point(_visitor: &mut V, _point: &Point) where V: Visitor + ?Sized, { } // `Visitable` is same as earlier # struct Arc { # center: Point, # radius: u32, # } # # struct Rectangle { # top_right: Point, # bottom_left: Point, # } # # struct Point { # x: i32, # y: i32, # } ``` -------------------------------- ### Invoking a visitor on a rectangle Source: https://docs.rs/despatma/latest/despatma/macro.visitor.html Demonstrates three distinct ways to invoke a visitor (PointCounter) on a Rectangle object. The first method is preferred for its generic nature. ```rust // Create a rectangle with bottom-left point (0, 0) and top-right point (10, 12) let rect = Rectangle::new(0, 0, 10, 12); let mut point_stats = PointCounter{ count: 0 }; // Invoke visitor on hierarchy options rect.apply(&mut point_stats); // 1 - Preferred visit_rectangle(&mut point_stats, &rect); // 2 point_stats.visit_rectangle(&rect); // 3 ``` -------------------------------- ### Using Trait Abstractions with Singleton Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html Demonstrates using a trait object for a singleton dependency by hinting the concrete type in the attribute. The trait must be annotated with #[auto_impl(&)] to support reference injection. ```rust use auto_impl::auto_impl; use despatma::dependency_container; #[auto_impl(&)] trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... # struct Sqlite; # # impl DataLayer for Sqlite { # fn get_user_name(&self, id: u32) -> String { # format!("User {}", id) # } # } # # struct Service { # data_layer: D, # } # # impl Service { # pub fn new(data_layer: D) -> Self { # Self { data_layer } # } # } #[dependency_container] impl Dependencies { #[Singleton(Sqlite)] fn data_layer(&self) -> impl DataLayer { Sqlite } fn service(&self, data_layer: impl DataLayer) -> Service { Service::new(data_layer) } } ``` -------------------------------- ### Abstract Factory Macro Usage Source: https://docs.rs/despatma/latest/despatma/attr.abstract_factory.html The basic syntax for applying the abstract_factory attribute macro to a trait. ```rust #[abstract_factory] ``` -------------------------------- ### Returning trait objects with #[Transient(Sqlite)] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Shows how to return trait objects from dependency methods. The `data_layer` method is annotated with `#[Transient(Sqlite)]` to hint the concrete type. This decouples `Service` from the concrete implementation of `DataLayer`, making unit testing easier and allowing runtime swapping. ```rust use despatma::dependency_container; trait DataLayer { fn get_user_name(&self, id: u32) -> String; } // Implementation details... #[dependency_container] impl Dependencies { #[Transient(Sqlite)] fn data_layer(&self) -> impl DataLayer { Sqlite } fn service(&self, data_layer: impl DataLayer) -> Service { Service::new(data_layer) } } ``` -------------------------------- ### Expanded output of #[interpolate_traits] Source: https://docs.rs/despatma/latest/despatma/attr.interpolate_traits.html Shows the expanded code after the `#[interpolate_traits]` macro is applied. The `TRAIT` and `CONCRETE` markers are replaced with each concrete type, generating separate `impl` blocks for `Factory`, `Factory`, and `Factory`, as well as `impl Element` for each concrete type. ```rust use despatma::{abstract_factory, interpolate_traits}; use std::fmt::Display; // GuiFactory and Factory are explained in the abstract_factory example struct GnomeFactory{} pub trait Factory { fn create(&self, name: String) -> Box; } #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} // Implement the factory method for each GUI element impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(GnomeButton::create(name)) } } impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(Gnome2Scroller::create(name)) } } impl Factory for GnomeFactory { fn create(&self, name: String) -> Box { Box::new(Gnome3Window::create(name)) } } pub trait Element { fn create(name: String) -> Self where Self: Sized; } impl Element for GnomeButton { fn create(name: String) -> Self { GnomeButton { name } } } impl Element for Gnome2Scroller { fn create(name: String) -> Self { Gnome2Scroller { name } } } impl Element for Gnome3Window { fn create(name: String) -> Self { Gnome3Window { name } } } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } struct GnomeButton { name: String, } impl Button for GnomeButton { fn click(&self) {} } struct Gnome2Scroller { name: String, } impl Scroller for Gnome2Scroller { fn scroll(&self, _x: i32, _y: i32) {} } struct Gnome3Window { name: String, } impl Window for Gnome3Window { fn resize(&self, _width: u32, _height: u32) {} } ``` -------------------------------- ### Generated Output for abstract_factory Source: https://docs.rs/despatma/latest/despatma/attr.abstract_factory.html The resulting code generated by the abstract_factory macro based on the provided input. ```rust use despatma::abstract_factory; use std::fmt::Display; // A factory method // `Element` is a trait defined by you. pub trait Factory { fn create(&self, name: String) -> Box; } // The abstract factory that also needs to implement `Display` and `Eq`. // The factory method above (`Factory`) is the first input. #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq + Factory + Factory + Factory { } pub trait Element { fn create() -> Self where Self: Sized; } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } ``` -------------------------------- ### Async Dependencies Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html The macro automatically makes parent dependencies async when they depend on an async method. ```rust use despatma::dependency_container; # use std::time::Duration; # use tokio::time::sleep; // Implementation details... # struct Config { # port: u32, # } # # impl Config { # async fn new() -> Self { # sleep(Duration::from_secs(1)).await; # Config { port: 8080 } # } # } # # struct Service; # # impl Service { # pub fn new(port: u32) -> Self { # Service # } # } #[dependency_container] impl MyContainer { async fn config(&self) -> Config { Config::new().await } fn service(&self, config: Config) -> Service { Service::new(config.port) } } ``` -------------------------------- ### Making the container public with #[dependency_container(pub)] Source: https://docs.rs/despatma/latest/despatma/attr.dependency_container.html Shows how to make the generated container struct public by adding a visibility modifier to the macro invocation. The macro supports all Rust visibility modifiers including `pub`, `pub(crate)`, `pub(super)`, and `pub(in path)`. ```rust use despatma::dependency_container; #[dependency_container(pub)] impl MyContainer { } ``` -------------------------------- ### Use no_default option Source: https://docs.rs/despatma/latest/src/despatma/lib.rs.html The no_default option forces the visitor to implement the visit method, removing the default trait implementation. ```rust use despatma::visitor; # # struct Arc { # center: Point, # radius: u32, # } # # struct Rectangle { # top_right: Point, # bottom_left: Point, # } # # struct Point { # x: i32, # y: i32, # } visitor!( #[ helper_tmpl = {visitor.visit_point(&arc.center);}, no_default, ] Arc, #[ helper_tmpl = { visitor.visit_point(&rectangle.top_right); visitor.visit_point(&rectangle.bottom_left); }, ] Rectangle, Point, ); ``` -------------------------------- ### abstract_factory Attribute Macro Source: https://docs.rs/despatma/latest/despatma/attr.abstract_factory.html The `abstract_factory` attribute macro transforms a trait into an Abstract Factory. It requires a factory method as the first argument and a list of element traits to create. The macro expands the trait to implement the factory trait for each element. ```APIDOC ## abstract_factory Attribute Macro ### Description Turns a `trait` into an Abstract Factory. The trait can optionally have super-traits. This macro is generally used with the `interpolate_traits` macro. ### Usage Apply the `#[abstract_factory]` attribute to a trait definition. The first argument to the macro is the factory method trait, and the remaining arguments are the element traits the factory will create. ### Parameters - **factory_method** (trait) - Required - The trait that defines the factory method (e.g., `Factory`). - **elements** (trait list) - Required - The element traits the factory will create (e.g., `dyn Button`, `dyn Scroller`, `dyn Window`). ### Example Input ```rust use despatma::abstract_factory; use std::fmt::Display; // A factory method // `Element` is a trait defined by you. pub trait Factory { fn create(&self, name: String) -> Box; } // The abstract factory that also needs to implement `Display` and `Eq`. // The factory method above (`Factory`) is the first input. #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq {} pub trait Element { fn create() -> Self where Self: Sized; } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } ``` ### Output This will create the following code: ```rust use despatma::abstract_factory; use std::fmt::Display; // A factory method // `Element` is a trait defined by you. pub trait Factory { fn create(&self, name: String) -> Box; } // The abstract factory that also needs to implement `Display` and `Eq`. // The factory method above (`Factory`) is the first input. #[abstract_factory(Factory, dyn Button, dyn Scroller, dyn Window)] pub trait GuiFactory: Display + Eq + Factory + Factory + Factory { } pub trait Element { fn create() -> Self where Self: Sized; } pub trait Button: Element { fn click(&self); } pub trait Scroller: Element { fn scroll(&self, x: i32, y: i32); } pub trait Window: Element { fn resize(&self, width: u32, height: u32); } ``` ```