Crates.io | template-builder |
lib.rs | template-builder |
version | 0.1.0 |
source | src |
created_at | 2022-06-21 18:06:53.644425 |
updated_at | 2022-06-21 18:06:53.644425 |
description | A rust library for making idiomatic, declarative, builder-like patterns that use the struct literal syntax. |
homepage | |
repository | https://github.com/aggalex/template |
max_upload_size | |
id | 610419 |
size | 7,896 |
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
#[template]
pub struct Box {
orientation: Orientation,
spacing: i32,
padding: i32,
margin: i32,
}
#[template]
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
impl Template for Box {
type Output = some_other_lib::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 = some_other_lib::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.
trait Container {
fn child<T, W>(&self) -> T
where T: Template<Output = W>,
W: some_other_lib::Widget;
}
impl Container for some_other_lib::Box {
fn child<T, W>(&self) -> T
where T: Template<Output = W>,
W: some_other_lib::Widget
{
let this = self.clone();
let out = Button::default(); // The template
out.on_create(move |w| this.add(w)); // provided by the #[template] annotation
out
}
}
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:
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
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.:trait Templatable {
type New: Template<Output = Self>;
}
// ...so that
use some_other_lib::Box;
Box::New {
//...
} ();
std::ops::FnOnce
implementations,
so they automatically label Struct { /*...*/ } ( /*...*/ )
syntaxes as wrong
when they are perfectly legal and fine as per the std::ops::FnOnce
trait 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.