state-m 0.4.4

The library implements convenient state distribution and management mechanisms, facilitating collaborative work between components.
Documentation

state-m


The library implements convenient state distribution and management mechanisms, facilitating collaborative work between components.

Features

  • Separation of read-write, initiators and responders of state changes hold different data structures.
  • Duplicate filtering, by default, duplicate states do not trigger state changes.
  • State transition, supports type conversion of subscription state changes.
  • Timing control, supports waiting for all responders to complete their responses.

Usage

  • Define 'Tag' enum to distinguish different initiators or responders, all initiators must use different tag values, all responders, and all responders do the same, a same tag value can be used by an initiator and a responder in the same state machine.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum Tag {
    A,
    B(usize)
}
  • Implement 'HasStateMachine' trait for you data structure, whether it's the initiator or responder of state change, maybe you should add some fields to your data structure.
#[derive(Debug, Default)]
struct Unit {
    ...
    lock: Mutex<()>,
    state_machine: StateMachine<Tag>,
}

#[async_trait]
impl HasStateMachine<Tag> for Unit {
    async fn lock(&self) -> MutexGuard<'_, ()> {
        self.lock.lock().await
    }

    async fn state_machine(&self) -> StateMachine<Tag> {
        self.state_machine.clone()
    }
}
  • If your data structure is also a responder of some state change, implement 'HasStateHandle' trait for your data structure. Then subscribe sources as needed. Unsubscription is optional, after your state machine is dropped, subscriptions are auto cleaned.
#[async_trait]
impl HasStateHandle<S, T, Tag> for Unit {
    async fn on_change(
        self: Arc<Self>,
        tag: Tag,
        new_value: T,
        old_value: T,
    ) -> Result<(), Box<dyn std::error::Error>> {
        ...
    }
unit_target
    .clone()
    .subscribe(unit_source.reader(&TagA::Hi).await, TagB::X)
    .await;
unit_target
    .clone()
    .subscribe::<String>(
        unit_source
            .reader_ex(&TagA::Hi, |s| Box::pin(async move { format!("Hi, {}", s) }))
            .await,
        TagB::Y,
    )
    .await;
  • Add state change initiators to your state machine, after added, you can get it from state machine by tag. Then change state as needed.
// add source to state machine
unit_source.add_source::<String>(TagA::Hi).await;
unit_source.add_source_ex::<String>(TagA::Hi, 100, "Hello").await;
// change state by need
unit_source
    .change::<String>(&TagA::Hi, "Wang".into())
    .await?;
unit_source.touch::<String>(&TagA::Hi).await?;
unit_source
    .modify(&TagA::Hi, |s| format!("Dear {}", s))
    .await?;
unit_source
    .wait_change::<String>(&TagA::Hi, "Zhang".into())
    .await?;
unit_source
    .wait_modify(&TagA::Hi, |s| format!("Dear {}", s))
    .await?;
  • Do unsubscrption as needed
unit_target.unsubscribe::<String>(&TagB::X).await;
unit_target.unsubscribe::<String>(&TagB::Y).await;
unit_source.del_source(&TagA::Hi).await;