3个不稳定版本
0.3.0 | 2023年12月12日 |
---|---|
0.0.2 | 2023年10月5日 |
0.0.1 | 2023年10月5日 |
#476 in 数据结构
每月25次下载
30KB
447 行
介绍
constructivism
是一个 Rust 样本库,旨在通过定义和操作Constructs序列来简化结构化数据的构建。本README文件提供了使用 constructivism
的概述以及如何使用 constructivist
库将其内联到您的项目中。
安装
要在您的Rust项目中使用Constructivism,请将其作为依赖项添加到您的 Cargo.toml
文件中
[dependencies]
constructivism = "0.0.2"
或者让cargo来完成这些工作
cargo add constructivism
Constructivism可以作为例如 your_library_constructivism
的内联到您的库中,位于 constructivist
包中。请参阅 说明。
指南
另请参阅 examples/tutorial.rs
入门
通常,您从以下内容开始
use constructivism::*;
Constructs和序列
1.1. Constructs:建构主义围绕Constructs的概念展开。您可以通过这种方式派生construct
#[derive(Construct)]
pub struct Node {
hidden: bool,
position: (f32, f32),
}
1.2 construct!
:您可以使用 construct!
宏来创建Constructs的实例。请注意每个参数开头的点,这是必需的,您会发现这种语法非常有用。
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 序列:可以在另一个构造符之前声明一个构造符。`constructivism
` 只包含空值,`()` 构造符。`Self -> Base
` 是 `constructivism
` 中的序列关系。可以省略序列声明,在这种情况下使用 `Self -> Nothing
`。如果你想在另一个有意义的构造符之上派生构造符,必须直接使用 `#[construct(/* 序列 */)]
` 属性来指定序列。
#[derive(Construct)]
#[construct(Rect -> Node)]
pub struct Rect {
size: (f32, f32),
}
1.4 构建序列:上述例子中的矩形序列变为 `Rect -> Node -> Nothing
`。可以在单个调用中构建整个序列。
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 参数:有不同的参数类型(传递给 `construct!(..)
` 的东西)
- 常见:如果没有传递给 `
construct!(..)
`,则使用 `Default::default()
` - 默认:如果没有传递给 `
construct!(..)
`,则使用提供的值 - 必需:必须传递给 `
construct!(..)
` - 跳过:不能传递给 `
construct!(..)
`,使用 `Default::default()` 或提供的值。在派生时使用 `#[param]
` 属性来配置行为。
#[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 传递参数:当传递参数给 `construct!(..)
` 时,必须传递序列所需的所有必需参数,否则将得到编译错误。可以省略非必需参数。
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);
}
设计和方法
2.1 设计和方法:每个构造符都有自己的设计。可以为构造符的设计实现方法
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 调用方法:可以在构造符的设计上调用方法。方法解析遵循序列顺序
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
}
段
3.1 段:段允许你在构造符的序列中定义和插入段
#[derive(Segment)]
pub struct Input {
disabled: bool,
}
#[derive(Construct)]
#[construct(Button -> Input -> Rect)]
pub struct Button {
pressed: bool
}
3.2 带有段的序列:按钮的序列变为 `Button -> Input -> Rect -> Node -> Nothing
`。可以在单个 `construct!
` 调用中实例化包含段的构造符的整个序列
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 段设计:段也有自己的设计。方法调用同样在序列顺序中解析。段的设计有一个泛型参数 - 下一个段/构造符,因此实现段的设计时必须遵守它。
impl<T> InputDesign<T> {
fn focus(&self, entity: Entity) {
/* do the focus stuff */
}
}
fn focus_button() {
let btn = Entity;
design!(Button).focus(btn);
}
属性
4.1 属性:通过推导构建或段,您还可以设置和获取与序列相关的项的属性
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<Node, (f32, f32)> */ = prop!(Button.position);
let size /* Prop<Rect, (f32, f32)> */ = prop!(Button.size);
let disabled /* Prop<Input, bool> */ = prop!(Button.disabled);
let pressed /* Prop<Button, bool */ = prop!(Button.pressed);
// You can read props. You have to pass exact item to the get()
let x = pos.get(&node).as_ref().0;
let w = size.get(&rect).as_ref().0;
let is_disabled = *disabled.get(&input).as_ref();
let is_pressed = *pressed.get(&button).as_ref();
assert_eq!(0., x);
assert_eq!(100., w);
assert_eq!(false, is_disabled);
assert_eq!(false, is_pressed);
// You can set props. You have to pass exact item to set()
pos.set(&mut node, (1., 1.));
size.set(&mut rect, (10., 10.));
disabled.set(&mut input, true);
pressed.set(&mut button, true);
assert_eq!(node.position.0, 1.);
assert_eq!(rect.size.0, 10.);
assert_eq!(input.disabled, true);
assert_eq!(button.pressed, true);
}
4.2 扩展属性:如果您有构建类型的字段,您也可以访问这些字段的属性
#[derive(Construct, Default)]
#[construct(Vec2 -> Nothing)]
pub struct Vec2 {
x: f32,
y: f32,
}
#[derive(Construct)]
#[construct(Node2d -> Nothing)]
pub struct Node2d {
#[prop(construct)] // You have to mark expandable props with #[prop(construct)]
position: Vec2,
}
fn modify_position_x() {
let mut node = construct!(Node2d);
assert_eq!(node.position.x, 0.);
assert_eq!(node.position.y, 0.);
let x = prop!(Node2d.position.x);
x.set(&mut node, 100.);
assert_eq!(node.position.x, 100.);
assert_eq!(node.position.y, 0.);
}
自定义构建器
5.1 自定义构建器:有时您可能需要为外部类型实现构建或提供自定义构建器。您可以使用 derive_construct!
来实现此目的
pub struct ProgressBar {
min: f32,
val: f32,
max: f32,
}
impl ProgressBar {
pub fn min(&self) -> f32 {
self.min
}
pub fn set_min(&mut self, min: f32) {
self.min = min;
if self.max < min {
self.max = min;
}
if self.val < min {
self.val = min;
}
}
pub fn max(&self) -> f32 {
self.max
}
pub fn set_max(&mut self, max: f32) {
self.max = max;
if self.min > max {
self.min = max;
}
if self.val > max {
self.val = max;
}
}
pub fn val(&self) -> f32 {
self.val
}
pub fn set_val(&mut self, val: f32) {
self.val = val.max(self.min).min(self.max)
}
}
derive_construct! {
// Sequence
seq => ProgressBar -> Rect;
// Constructor, all params with default values
construct => (min: f32 = 0., max: f32 = 1., val: f32 = 0.) -> {
if max < min {
max = min;
}
val = val.min(max).max(min);
Self { min, val, max }
};
// Props using getters and setters
props => {
min: f32 = [min, set_min];
max: f32 = [max, set_max];
val: f32 = [val, set_val];
};
}
5.2 使用自定义构建器:在创建实例时将调用提供的构建器
fn create_progress_bar() {
let (pb, _, _) = construct!(ProgressBar { .val: 100. });
assert_eq!(pb.min, 0.);
assert_eq!(pb.max, 1.);
assert_eq!(pb.val, 1.);
}
5.3 自定义构建属性:在上面的示例中,derive_construct!
使用获取器和设置器声明属性。当您使用 Prop::get
和 Prop::set
fn modify_progress_bar() {
let (mut pb, _, _) = construct!(ProgressBar {});
let min = prop!(ProgressBar.min);
let val = prop!(ProgressBar.val);
let max = prop!(ProgressBar.max);
assert_eq!(pb.val, 0.);
val.set(&mut pb, 2.);
assert_eq!(pb.val, 1.0); //because default for max = 1.0
min.set(&mut pb, 5.);
max.set(&mut pb, 10.);
assert_eq!(pb.min, 5.);
assert_eq!(pb.val, 5.);
assert_eq!(pb.max, 10.);
}
5.4 推导段:您可以以类似的方式推导段
pub struct Range {
min: f32,
max: f32,
val: f32,
}
derive_segment! {
// use `seg` to provide type you want to derive Segment
seg => Range;
construct => (min: f32 = 0., max: f32 = 1., val: f32 = 0.) -> {
if max < min {
max = min;
}
val = val.min(max).max(min);
Self { min, val, max }
};
// Props using fields directly
props => {
min: f32 = value;
max: f32 = value;
val: f32 = value;
};
}
#[derive(Construct)]
#[construct(Slider -> Range -> Rect)]
pub struct Slider;
fn create_slider() {
let (slider, range, _, _) = construct!(Slider {
.val: 10.
});
assert_eq!(range.min, 0.0);
assert_eq!(range.max, 1.0);
assert_eq!(range.val, 1.0);
}
限制
- 仅公共结构体(或具有
constructable!
的枚举) - 尚不支持泛型(看起来非常可能)
- 整个继承树参数数量有限(默认版本编译为16,测试为64)
- 仅静态结构体/枚举(无生命周期)
成本
我没有进行任何压力测试。它应该运行得相当快:没有堆分配,只有一些对每个 construct!
定义的属性在每个深度级别进行的解引用调用。冷编译时间随着参数数量限制的增加而增长(64个参数为1.5分钟),但二进制文件的大小没有变化。
路线图
- 将
#![forbid(missing_docs)]
添加到每个存储库的根目录 - docstring 绕过
- 泛型
- 联合参数,因此您只能从组中传递一个参数。例如,范围可以具有
min
、max
、abs
和rel
构建器参数,并且您不能同时传递abs
和rel
。 - 嵌套构建推理(看起来是可能的)
#[derive(Construct, Default)]
pub struct Vec2 {
x: f32,
y: f32
}
#[derive(Construct)]
pub struct Div {
position: Vec2,
size: Vec2,
}
fn step_inference() {
let div = construct!(Div {
position: {{ x: 23., y: 20. }},
size: {{ x: 23., y: 20. }}
})
}
贡献
我欢迎对Constructivism的贡献!如果您想贡献或有任何问题,请随时打开一个问题或提交一个拉取请求。
许可
constructivism
根据以下任一许可进行双许可
- MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
- Apache 许可证,版本 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
这意味着您可以选择您喜欢的许可!这种双许可方法是Rust生态系统中的事实标准,并且有很好的理由包括两者。
依赖项
~0.6–1.2MB
~28K SLoC