## Introduction
`constructivism` is a Rust sample-library designed to simplify the construction of structured data by defining and manipulating sequences of Constructs. This README provides an overview of how to use `constructivism` and how it can be inlined into you project using `constructivist` library.
## Installation
To use Constructivism in your Rust project, add it as a dependency in your `Cargo.toml` file:
```toml
[dependencies]
constructivism = "0.0.2"
```
Or let the cargo do the stuff:
```bash
cargo add constructivism
```
Constructivism can be inlined into you library as for example `your_library_constructivism` within `constructivist` crate. See [instructions](./crates/constructivist).
## Guide
See also [examples/tutorial.rs](examples/tutorial.rs)
### Getting Started
Usually you start with
```rust
use constructivism::*;
```
### Constructs and Sequences
1.1. **Constructs**: Constructivism revolves around the concept of Constructs. You can derive construct like this:
```rust
#[derive(Construct)]
pub struct Node {
hidden: bool,
position: (f32, f32),
}
```
1.2 **`construct!`**: You can use the `construct!` macro to create instances of Constructs. Please ***note*** the dots at the beginning of the each param, they are required and you will find this syntax quite useful.
```rust
fn create_node() {
let node = construct!(Node {
.position: (10., 10.),
.hidden: true
});
assert_eq!(node.position.0, 10.);
assert_eq!(node.hidden, true);
}
```
1.3 **Sequences**: A Construct can be declared only in front of another Construct. `constructivism` comes with only Nothing, () construct. The `Self -> Base` relation called Sequence in `constructivism`. You can omit the Sequence declaration, `Self -> Nothing` used in this case. If you want to derive Construct on the top of another meaningful Construct, you have to specify Sequence directly with `#[construct(/* Sequence */)]` attribute.
```rust
#[derive(Construct)]
#[construct(Rect -> Node)]
pub struct Rect {
size: (f32, f32),
}
```
1.4 **Constructing Sequences**: The Sequence for the Rect in example above becomes `Rect -> Node -> Nothing`. You can `construct!` the entire sequence within a single call:
```rust
fn create_sequence() {
let (rect, node /* nothing */) = construct!(Rect {
.hidden, // You can write just `.hidden` instead of `.hidden: true`
.position: (10., 10.),
.size: (10., 10.),
});
assert_eq!(rect.size.0, 10.);
assert_eq!(node.position.1, 10.);
assert_eq!(node.hidden, false);
}
```
1.5 **Params**: There are different kind of Params (the things you passing to `construct!(..)`):
- Common: use `Default::default()` if not passed to `construct!(..)`
- Default: use provided value if not passed to `construct!(..)`
- Required: must be passed to `construct!(..)`
- Skip: can't be passed to `construct!(..)`, use Default::default() or provided value
You configure behavior using `#[param]` attribute when deriving:
```rust
#[derive(Construct)]
#[construct(Follow -> Node)]
pub struct Follow {
offset: (f32, f32), // Common, no #[param]
#[param(required)] // Required
target: Entity,
#[param(default = Anchor::Center)] // Default
anchor: Anchor,
#[param(skip)] // Skip with Default::default()
last_computed_distance: f32,
#[param(skip = FollowState::None)] // Skip with provided value
state: FollowState,
}
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Entity;
pub enum Anchor {
Left,
Center,
Right,
}
pub enum FollowState {
None,
Initialized(f32)
}
```
1.6 **Passing params**: When passing params to `construct!(..)` you have to pass all required for Sequence params, or you will get the compilation error. You can omit non-required params.
```rust
fn create_elements() {
// omit everything, default param values will be used
let (rect, node, /* nothing */) = construct!(Rect);
assert_eq!(node.hidden, false);
assert_eq!(rect.size.0, 0.);
// you have to pass target to Follow, the rest can be omitted..
let (follow, node) = construct!(Follow {
.target: Entity
});
assert_eq!(follow.offset.0, 0.);
assert_eq!(node.hidden, false);
// ..or specified:
let (follow, node) = construct!(Follow {
.hidden,
.target: Entity,
.offset: (10., 10.),
// last_computed_distance param is skipped, uncommenting
// the next line will result in compilation error
// error: no field `last_computed_distance` on type `&follow_construct::Params`
// .last_computed_distance: 10.
});
assert_eq!(follow.offset.0, 10.);
assert_eq!(node.hidden, true);
}
```
### Design and Methods
2.1 **Designs and Methods**: Every Construct has its own Design. You can implement methods for a Construct's design:
```rust
impl NodeDesign {
pub fn move_to(&self, entity: Entity, position: (f32, f32)) { }
}
impl RectDesign {
pub fn expand_to(&self, entity: Entity, size: (f32, f32)) { }
}
```
2.2 **Calling Methods**: You can call methods on a Construct's design. Method resolution follows the sequence order:
```rust
fn use_design() {
let rect_entity = Entity;
design!(Rect).expand_to(rect_entity, (10., 10.));
design!(Rect).move_to(rect_entity, (10., 10.)); // move_to implemented for NodeDesign
}
```
### Segments
3.1 **Segments**: Segments allow you to define and insert segments into a Construct's sequence:
```rust
#[derive(Segment)]
pub struct Input {
disabled: bool,
}
#[derive(Construct)]
#[construct(Button -> Input -> Rect)]
pub struct Button {
pressed: bool
}
```
3.2 **Sequence with Segments**: The Sequence for Button becomes `Button -> Input -> Rect -> Node -> Nothing`. You can instance the entire sequence of a Construct containing segments within a single `construct!` call:
```rust
fn create_button() {
let (button, input, rect, node) = construct!(Button {
.disabled: true
});
assert_eq!(button.pressed, false);
assert_eq!(input.disabled, true);
assert_eq!(rect.size.0, 100.);
assert_eq!(node.position.0, 0.);
}
```
3.3 **Segment Design**: Segment has its own Design as well. And the method call resolves within the Sequence order as well. Segment's designs has one generic parameter - the next segment/construct, so you have to respect it when implement Segment's Design:
```rust
impl InputDesign {
fn focus(&self, entity: Entity) {
/* do the focus stuff */
}
}
fn focus_button() {
let btn = Entity;
design!(Button).focus(btn);
}
```
### Props
4.1 **Props**: By deriving Constructs or Segments you also get the ability to set and get properties on items with respect of Sequence:
```rust
fn button_props() {
let (mut button, mut input, mut rect, mut node) = construct!(Button);
// You can access to props knowing only the top-level Construct
let pos /* Prop */ = prop!(Button.position);
let size /* Prop */ = prop!(Button.size);
let disabled /* Prop */ = prop!(Button.disabled);
let pressed /* Prop