Template.rs

A rust library for making idiomatic, declarative, builder-like patterns that use the struct literal syntax.

No function-like macros required!

How to use

To make a template, just as you would make a builder, you have to consider the following:

  1. All the parameters
  2. The default state of the template
  3. What the template builds into.
  4. Its relationship with other objects.

You can define a template using the #[template] annotation to your template model struct, and the Template trait.

1. Defining the parameters

Simply use the the #[template] annotation on a struct with all the parameters as its fields

```rust

[template]

pub struct Box { orientation: Orientation, spacing: i32, padding: i32, margin: i32, }

[template]

pub struct Button { padding: i32, margin: i32, style: StyleDescriptor, text: String } ```

2. The default state of the template

By default, this library defines the default state of your template by #[derive(Default)]. Custom Default implementations are also planned.

3. What the template builds into

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
}

}

```

4. Its relationship with other objects.

Define different default states by dependency injection by objects relative to the template's target.

```rust trait Container { fn child(&self) -> T where T: Template, W: someotherlib::Widget; }

impl Container for someotherlib::Box { fn child(&self) -> T where T: Template, W: someotherlib::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 } } ```

Using the template

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 })

Current Problems and Future Plans

  1. Allow the user to define custom default states for templates
  2. Create a 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 considerstd::ops::FnOnceimplementations, so they automatically labelStruct { /.../ } ( /.../ )syntaxes as wrong when they are perfectly legal and fine as per thestd::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.