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:
[]
= "0.0.2"
Or let the cargo do the stuff:
Constructivism can be inlined into you library as for example your_library_constructivism
within constructivist
crate. See instructions.
Guide
See also examples/tutorial.rs
Getting Started
Usually you start with
use *;
Constructs and Sequences
1.1. Constructs: Constructivism revolves around the concept of Constructs. You can derive construct like this:
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.
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.
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:
1.5 Params: There are different kind of Params (the things you passing to construct!(..)
):
- Common: use
Default::default()
if not passed toconstruct!(..)
- 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:
;
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.
Design and Methods
2.1 Designs and Methods: Every Construct has its own Design. You can implement methods for a Construct's design:
2.2 Calling Methods: You can call methods on a Construct's design. Method resolution follows the sequence order:
Segments
3.1 Segments: Segments allow you to define and insert segments into a Construct's sequence:
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:
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:
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:
4.2 Expand props: If you have field with Construct type, you can access this fields props as well:
Custom Constructors
5.1 Custom Constructors: Sometimes you may want to implement Construct for a foreign type or provide a custom constructor. You can use derive_construct!
for this purpose:
derive_construct!
5.2 Using Custom Constructors: The provided constructor will be called when creating instances:
5.3 Custom Construct Props: In the example above derive_construct!
declares props using getters and setters. This setters and getters are called when you use Prop::get
and Prop::set
5.4 Deriving Segments: You can derive Segments in a similar way:
derive_segment!
;
Limitations
- only public structs (or enums with
constructable!
) - no generics supported yet (looks very possible)
- limited number of params for the whole inheritance tree (default version compiles with 16, tested with 64)
- only static structs/enums (no lifetimes)
Cost
I didn't perform any stress-tests. It should run pretty fast: there is no heap allocations, only some deref calls per construct!
per defined prop per depth level. Cold compilation time grows with number of params limit (1.5 mins for 64), but the size of the binary doesn't changes.
Roadmap
- add
#![forbid(missing_docs)]
to the root of each crate - docstring bypassing
- generics
- union params, so you can pas only one param from group. For example, Range could have
min
,max
,abs
andrel
constructor params, and you can't passabs
andrel
both. - nested construct inference (looks like possible):
Contributing
I welcome contributions to Constructivism! If you'd like to contribute or have any questions, please feel free to open an issue or submit a pull request.
License
The constructivism
is dual-licensed under either:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
This means you can select the license you prefer! This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are very good reasons to include both.