A rust library for making idiomatic, declarative, builder-like patterns that use the struct literal syntax.
No function-like macros required!
To make a template, just as you would make a builder, you have to consider the following:
You can define a template using the #[template]
annotation
to your template model struct, and the Template
trait.
Simply use the the #[template]
annotation on a struct with
all the parameters as its fields
```rust
pub struct Box { orientation: Orientation, spacing: i32, padding: i32, margin: i32, }
pub struct Button { padding: i32, margin: i32, style: StyleDescriptor, text: String } ```
By default, this library defines the default state of your
template by #[derive(Default)]
. Custom Default
implementations
are also planned.
Use the Template
trait to define what the template builds into, and how
```rust
impl Template for Box { type Output = someotherlib::Box;
fn define(self) -> Self::Output {
let mut this = some_other_lib::Box::new();
this.padding = self.padding;
//...
this
}
}
impl Template for Button { type Output = someotherlib::Button;
fn define(self) -> Self::Output {
let mut this = some_other_lib::Button::new();
this.padding = self.padding;
//...
this
}
}
```
Define different default states by dependency injection by objects relative to the template's target.
```rust
trait Container {
fn child
impl Container for someotherlib::Box {
fn child
All annotated templates implement the following traits for building the target types:
- for<A, F: FnOnce(Self::Output) -> A> FnOnce(F) -> A
- FnOnce() -> Self::Output
Those traits can be invoked right after the struct literal in a "currying" fashion.
This is a simple example of idiomatic templates:
```rust Box { orientation: Orientation::HORIZONTAL, padding: 6, spacing: 6, ..Default::default() } (|w| { Box { orientation: Orientation::VERTICAL, spacing: 6, ..w.child() } (|w| {
Button {
text: "Column btn 1",
..w.child()
}();
// Function call constructs button
// and adds it to the box as per the
// ..w.child() injection directive
Button {
text: "Column btn 2"
..w.child()
}();
}); // function call runs the lambda argument
// on the output and then returns it
Button {
text: "Big btn",
..w.child()
}();
})
// expected result: // |--------------|---------| // | Column btn 1 | | // |--------------| Big Btn | // | Column btn 2 | | // |--------------|---------| ```
In case you don't want to use direct function calls,
you can use the following equivalent methods:
- fn build<A>(self, F: impl FnOnce(Self::Output) -> A) -> A
- fn create(self) -> Self::Output
rust
Box {
orientation: Orientation::HORIZONTAL,
padding: 6,
spacing: 6,
..Default::default()
}.build(|w| { // creations with lambdas use the "build" method
Button {
text: "Hello World",
..w.child()
}.create(); // creations without arguments use the "create" method
})
Templatable
trait associates the Output of a template with its template,
so that you don't have to associate it yourself. Eg.:
```rust
trait Templatable {
type New: Template// ...so that
use someotherlib::Box;
Box::New {
//...
} ();
``
3. Currently some type analysers do not consider
std::ops::FnOnceimplementations,
so they automatically label
Struct { /.../ } ( /.../ )syntaxes as wrong
when they are perfectly legal and fine as per the
std::ops::FnOncetrait definition.
For example, in the jetbrains Rust Plugin. Until such problems are solved, either use a
different analyser, or use the alternative
.build()and
.create()` methods.