2 releases

0.1.1 Jun 27, 2024
0.1.0 Jun 26, 2024

#366 in Embedded development

Custom license

14KB
118 lines

fieldset

Maintenance CI docs

This library tracks field modifications as data. It is intended to provide a bounded-space alternative to event listeners, designed for but not restricted to usage in embedded systems.

It works by deriving a FieldType, a FieldSetter trait and multiple FieldSet types from a struct.

  • The FieldType is an enum where each variant corresponds to each field of the structure.
  • The FieldSetter trait consists of one setter method for each field.
  • The FieldSet types implement the FieldSetter trait and provide an iterator interface where each item is a FieldType instance corresponding to modified fields.

Subsystems can use the FieldSetter interface to "modify" fields of the original parameter structure and then the FieldSet can be iterated upon to notify other subsystems that the fields were modified. This allows for implementing event-driven architectures by batching modifications and then notifying afterwards.

There are multiple FieldSetter implementations with different tradeoffs regarding iteration and backup storage.

  • OptFieldSet is backed by a derived struct where each field is converted to an Option. Each iteration goes through all fields and is therefore suitable for smaller structures or frequent modifications.
  • BitFieldSet is backed by an iteration array of FieldType with length equal to the number of fields, and a bitfield that tracks which fields have been modified. Iteration is optimal and only goes through exactly as many fields as were modified. Has the drawback that each field can only be modified once before iteration and subsequent modifications are ignored. This is often a good compromise.
  • PerfFieldSet is backed by an array of FieldType of length equal to the number of fields and a complementary array that tracks which fields have been modified and their current position in the iteration array. Iteration is optimal and only goes through exactly as many fields as were modified. Fields can be modified multiple times and only the latest modification applies. Has the drawback of the extra space needed to track the multiple modifications.

The library currently requires the usage of the nightly impl_trait_in_assoc_type feature.

Example

#![feature(impl_trait_in_assoc_type)]
use fieldset::{FieldSetter, FieldSet};

#[derive(Default, FieldSet)]
struct SubModel {
    a: f32,
    b: u32
}

#[derive(Default, FieldSet)]
struct DomainModel {
    #[fieldset]
    sub: SubModel,
    c: f32
}

fn sub_modifier(mut model: impl SubModelFieldSetter, i: u32) {
    model.b().set(i);
}

fn modifier(mut model: impl DomainModelFieldSetter, i: u32) {
    model.c().set(i as f32);
    sub_modifier(model.sub(), i);
}

fn example() {
    let mut model = DomainModel::default();
    for i in 0..10 {
        let mut field_set = DomainModelPerfFieldSet::default();
        modifier(&mut field_set, i);
        let mut iter = field_set.into_iter();
        for field_change in iter.clone() {
            model.apply(field_change);
        }
        assert_eq!(iter.next(), Some(DomainModelFieldType::C(i as f32)));
        assert_eq!(iter.next(), Some(DomainModelFieldType::Sub(SubModelFieldType::B(i))));
        assert_eq!(model.c, i as f32);
        assert_eq!(model.sub.b, i);
    }
}

Dependencies

~0.7–1.2MB
~23K SLoC