Crates.io | hotdrink-rs |
lib.rs | hotdrink-rs |
version | 0.1.3 |
source | src |
created_at | 2021-04-14 11:12:20.227009 |
updated_at | 2021-05-04 12:35:30.901861 |
description | An implementation of HotDrink in Rust. |
homepage | |
repository | https://github.com/HotDrink/hotdrink-rs |
max_upload_size | |
id | 383942 |
size | 354,036 |
HotDrink implemented in Rust.
HotDrink lets you describe relations between values declaratively and how to enforce them, and can then automatically do so when the value of a variable changes.
Before getting started, here is a quick introduction to the terminology and how it works.
A Component
is a set of variables with a set of Constraint
s between them.
A Constraint
consists of a set of Method
s that are essentially functions that enforce the constraint
by reading from some subset of the variables of the Component
and writing to another.
Components
can be gathered in a ConstraintSystem
, which provides an API
for interacting with multiple Component
s at once, such as update
.
A component is a collection of variables and constraints between them that should be enforced.
One can easily be created by using the [component!
] macro, as shown in the example below.
A constraint represents a relation between variables we want to maintain.
It contains a collection of constraint satisfaction methods that describe the different ways to do so.
In the example, we want the relation a + b = c
to hold at all times.
One way to enforce it is to re-compute a + b
and set c
to that value.
A constraint satisfaction method describes one way to enforce a constraint. It reads the values of some variables, and write to others.
Add the following to your Cargo.toml
:
hotdrink-rs = "0.1.1"
Then you are ready to begin!
use hotdrink_rs::{component, model::ConstraintSystem, ret, Event};
// Define a set of variables and relations between them
let mut component = component! {
// Define a component `Component`.
component Component {
// Define variables and their default values.
// The value can be omitted for any type that implements `Default`.
let a: i32 = 0, b: i32, c: i32 = 3;
// Define a constraint `Sum` that must hold between variables.
constraint Sum {
// Provide three ways to enforce the constraint.
// Only one will be selected, so each one *MUST* enforce the constraint.
abc(a: &i32, b: &i32) -> [c] = ret![*a + *b];
acb(a: &i32, c: &i32) -> [b] = ret![*c - *a];
bca(b: &i32, c: &i32) -> [a] = ret![*c - *b];
}
}
};
// Describe what should happen when `a` changes.
component.subscribe("a", |event| match event {
Event::Pending => println!("A new value for `a` is being computed"),
Event::Ready(value) => println!("New value for `a`: {}", value),
Event::Error(errors) => println!("Computation for `a` failed: {:?}", errors),
});
// Change the value of `a`
component.set_variable("a", 3);
// Enforce all the constraints by selecting a method for each one,
// and then executing the methods in topological order.
component.update();
// Add the component to a constraint system.
// One constraint system can contain many components.
let mut cs = ConstraintSystem::new();
cs.add_component(component);
// Update every component in the constraint system.
cs.update();
The project uses multiple nightly features, and must be built using nightly Rust.
I recommend using rustup
, which can be downloaded here.
The examples in ./examples
can then be run with cargo run --example <name>
.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.