cleanup some LLM crud
This commit is contained in:
@@ -38,7 +38,7 @@
|
|||||||
#![deny(dead_code, unreachable_patterns)]
|
#![deny(dead_code, unreachable_patterns)]
|
||||||
|
|
||||||
use smarm::run;
|
use smarm::run;
|
||||||
use smarm::statem::{spawn, Cx, Machine, Reply, Resolution, StatemRef};
|
use smarm::gen_statem::{spawn, Cx, Machine, Reply, Resolution, StatemRef};
|
||||||
|
|
||||||
// === user types ============================================================
|
// === user types ============================================================
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
use smarm::gen_statem;
|
use smarm::gen_statem;
|
||||||
use smarm::run;
|
use smarm::run;
|
||||||
use smarm::statem::Reply;
|
use smarm::gen_statem::Reply;
|
||||||
|
|
||||||
// === user types (identical to statem_fused.rs) =============================
|
// === user types (identical to statem_fused.rs) =============================
|
||||||
|
|
||||||
|
|||||||
@@ -1,152 +0,0 @@
|
|||||||
//! A hand-written `gen_statem`, written directly against the chunk-1 primitives
|
|
||||||
//! in `smarm::statem` — no macro. This is the RFC 017 `Switch` example, and its
|
|
||||||
//! purpose is to be the **evaluation artifact**: it shows exactly the shape the
|
|
||||||
//! deferred `statem!` macro would have to generate, so we can judge what the
|
|
||||||
//! macro actually buys before committing to building one.
|
|
||||||
//!
|
|
||||||
//! Run with: `cargo run --example statem_switch`
|
|
||||||
//!
|
|
||||||
//! Sections are tagged // USER (you'd hand-write this with or without a macro)
|
|
||||||
//! and // MACRO (the boilerplate a `statem!` would emit for you).
|
|
||||||
|
|
||||||
use smarm::run;
|
|
||||||
use smarm::statem::{self, Cx, Machine, Reply, Resolution, StatemRef};
|
|
||||||
|
|
||||||
// ---- USER: the four hand-written types ------------------------------------
|
|
||||||
// In the macro world these are unchanged — the macro never generates them.
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
||||||
enum Switch {
|
|
||||||
Off,
|
|
||||||
On,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Counts {
|
|
||||||
flips: u32,
|
|
||||||
enters: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SwitchCast {
|
|
||||||
Flip,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SwitchCall {
|
|
||||||
GetCount(Reply<u32>),
|
|
||||||
GetEnters(Reply<u32>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- MACRO: the unified event ---------------------------------------------
|
|
||||||
// The user's two message enums folded together. Chunk 1 has no internal
|
|
||||||
// variants yet; chunks 2–3 add `StateTimeout` / `Timeout(name)` here.
|
|
||||||
|
|
||||||
enum Ev {
|
|
||||||
Cast(SwitchCast),
|
|
||||||
Call(SwitchCall),
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- MACRO: the machine struct + state cell -------------------------------
|
|
||||||
// `state` is the private cell; the `handle` body below is its sole writer.
|
|
||||||
|
|
||||||
struct SwitchSm {
|
|
||||||
state: Switch,
|
|
||||||
data: Counts,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SwitchSm {
|
|
||||||
// MACRO: `Switch::start` in the RFC; spawns the actor, hands back a ref.
|
|
||||||
fn start(init: Switch, data: Counts) -> StatemRef<SwitchSm> {
|
|
||||||
statem::spawn(SwitchSm { state: init, data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// MACRO: the `enter` dispatch, assembled from the per-state `enter` arms.
|
|
||||||
// USER wrote the arm bodies (`data.enters += 1`); the macro wrote the match
|
|
||||||
// and the `()` return. `enter` runs side effects only — it cannot transition.
|
|
||||||
fn enter(&mut self, s: Switch, _cx: &mut Cx<Ev>) {
|
|
||||||
match s {
|
|
||||||
Switch::Off => self.data.enters += 1,
|
|
||||||
Switch::On => self.data.enters += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Machine for SwitchSm {
|
|
||||||
type Ev = Ev;
|
|
||||||
|
|
||||||
// MACRO: run the initial state's enter.
|
|
||||||
fn on_start(&mut self, cx: &mut Cx<Ev>) {
|
|
||||||
let s = self.state;
|
|
||||||
self.enter(s, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle(&mut self, ev: Ev, cx: &mut Cx<Ev>) {
|
|
||||||
let prev = self.state;
|
|
||||||
|
|
||||||
// MACRO: the `(state, event)` dispatch table. USER wrote each arm tail
|
|
||||||
// (the body + the ending state tag); the macro qualified bare tags
|
|
||||||
// (`On` -> `Switch::On`), wrapped them in `.into()`, assembled the match,
|
|
||||||
// and — in the real macro — would have edge-checked each tail tag
|
|
||||||
// against the `transitions { Off => On, On => Off }` table at expand
|
|
||||||
// time. Here that check is on the honour system.
|
|
||||||
let next: Resolution<Switch> = match (self.state, ev) {
|
|
||||||
(Switch::Off, Ev::Cast(SwitchCast::Flip)) => {
|
|
||||||
self.data.flips += 1;
|
|
||||||
Switch::On.into()
|
|
||||||
}
|
|
||||||
(Switch::On, Ev::Cast(SwitchCast::Flip)) => Switch::Off.into(),
|
|
||||||
(Switch::Off, Ev::Call(SwitchCall::GetCount(r))) => {
|
|
||||||
r.reply(self.data.flips);
|
|
||||||
Switch::Off.into() // stay = return the current tag
|
|
||||||
}
|
|
||||||
(Switch::On, Ev::Call(SwitchCall::GetCount(r))) => {
|
|
||||||
r.reply(self.data.flips);
|
|
||||||
Switch::On.into()
|
|
||||||
}
|
|
||||||
(Switch::Off, Ev::Call(SwitchCall::GetEnters(r))) => {
|
|
||||||
r.reply(self.data.enters);
|
|
||||||
Switch::Off.into()
|
|
||||||
}
|
|
||||||
(Switch::On, Ev::Call(SwitchCall::GetEnters(r))) => {
|
|
||||||
r.reply(self.data.enters);
|
|
||||||
Switch::On.into()
|
|
||||||
}
|
|
||||||
// The macro would emit `_ => Resolution::Unhandled` here for a
|
|
||||||
// machine whose table is non-exhaustive; this one covers every
|
|
||||||
// (state, event) pair, so an added arm would be unreachable.
|
|
||||||
};
|
|
||||||
|
|
||||||
// MACRO: the resolution dispatch — identical in every generated machine.
|
|
||||||
match next {
|
|
||||||
Resolution::To(s) if s == prev => {} // stay: no enter, no reset
|
|
||||||
Resolution::To(s) => {
|
|
||||||
self.state = s; // <- sole writer of the state cell
|
|
||||||
// chunk 2: self.timers.clear_state_timeout();
|
|
||||||
self.enter(s, cx);
|
|
||||||
// chunk 3: cx.replay(&mut self.postponed);
|
|
||||||
}
|
|
||||||
Resolution::Postpone => {} // chunk 3
|
|
||||||
Resolution::Unhandled => cx.on_unhandled(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
run(|| {
|
|
||||||
// USER: the client side. With a macro these would be `sw.cast(..)` /
|
|
||||||
// `sw.call(SwitchCall::GetCount)`; without it, the `Ev::Cast` / `Ev::Call`
|
|
||||||
// wrap is explicit — note how mechanical it is.
|
|
||||||
let sw = SwitchSm::start(Switch::Off, Counts { flips: 0, enters: 0 });
|
|
||||||
|
|
||||||
sw.send(Ev::Cast(SwitchCast::Flip)).unwrap(); // Off -> On
|
|
||||||
sw.send(Ev::Cast(SwitchCast::Flip)).unwrap(); // On -> Off
|
|
||||||
|
|
||||||
let flips = sw.call(|r| Ev::Call(SwitchCall::GetCount(r))).unwrap();
|
|
||||||
let enters = sw.call(|r| Ev::Call(SwitchCall::GetEnters(r))).unwrap();
|
|
||||||
|
|
||||||
// RFC's stated end state after two flips: Counts { flips: 1, enters: 3 }
|
|
||||||
// (initial Off entry + On entry + Off entry).
|
|
||||||
println!("after two flips: flips={flips}, enters={enters}");
|
|
||||||
assert_eq!(flips, 1, "turned On once across the two flips");
|
|
||||||
assert_eq!(enters, 3, "initial Off + On + Off entries");
|
|
||||||
println!("ok");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -55,7 +55,7 @@ use std::marker::PhantomData;
|
|||||||
// Machine
|
// Machine
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// A finite state machine driven by the [`statem`](crate::statem) loop.
|
/// A finite state machine driven by the [`statem`](crate::gen_statem) loop.
|
||||||
///
|
///
|
||||||
/// The implementor owns its current state tag and its persistent data. It is
|
/// The implementor owns its current state tag and its persistent data. It is
|
||||||
/// the **sole writer** of the state cell (the loop never touches it): both
|
/// the **sole writer** of the state cell (the loop never touches it): both
|
||||||
@@ -426,13 +426,13 @@ macro_rules! gen_statem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl $sm {
|
impl $sm {
|
||||||
fn start(init: $State, data: $Data) -> $crate::statem::StatemRef<$sm> {
|
fn start(init: $State, data: $Data) -> $crate::gen_statem::StatemRef<$sm> {
|
||||||
$crate::statem::spawn($sm { state: init, data })
|
$crate::gen_statem::spawn($sm { state: init, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[deny(unreachable_patterns)]
|
#[deny(unreachable_patterns)]
|
||||||
fn enter(&mut self, state: $State, $cx: &mut $crate::statem::Cx<$Ev>) {
|
fn enter(&mut self, state: $State, $cx: &mut $crate::gen_statem::Cx<$Ev>) {
|
||||||
let $data = &mut self.data;
|
let $data = &mut self.data;
|
||||||
match state {
|
match state {
|
||||||
$( $est => { $ebody } ),+
|
$( $est => { $ebody } ),+
|
||||||
@@ -440,10 +440,10 @@ macro_rules! gen_statem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $crate::statem::Machine for $sm {
|
impl $crate::gen_statem::Machine for $sm {
|
||||||
type Ev = $Ev;
|
type Ev = $Ev;
|
||||||
|
|
||||||
fn on_start(&mut self, $cx: &mut $crate::statem::Cx<$Ev>) {
|
fn on_start(&mut self, $cx: &mut $crate::gen_statem::Cx<$Ev>) {
|
||||||
let s = self.state;
|
let s = self.state;
|
||||||
self.enter(s, $cx);
|
self.enter(s, $cx);
|
||||||
}
|
}
|
||||||
@@ -451,24 +451,24 @@ macro_rules! gen_statem {
|
|||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[deny(unreachable_patterns)] // conflicting rows must fail even though
|
#[deny(unreachable_patterns)] // conflicting rows must fail even though
|
||||||
// this match is external-macro-expanded
|
// this match is external-macro-expanded
|
||||||
fn handle(&mut self, ev: $Ev, $cx: &mut $crate::statem::Cx<$Ev>) {
|
fn handle(&mut self, ev: $Ev, $cx: &mut $crate::gen_statem::Cx<$Ev>) {
|
||||||
// Caller-named bindings (shared call-site hygiene, so row bodies
|
// Caller-named bindings (shared call-site hygiene, so row bodies
|
||||||
// can see them): `$cur` = current state tag, `$data` = &mut Data.
|
// can see them): `$cur` = current state tag, `$data` = &mut Data.
|
||||||
let $cur = self.state;
|
let $cur = self.state;
|
||||||
let $data = &mut self.data;
|
let $data = &mut self.data;
|
||||||
let next: $crate::statem::Resolution<$State> =
|
let next: $crate::gen_statem::Resolution<$State> =
|
||||||
$crate::gen_statem!(@arms ($Ev) ($cur, ev) [ ]
|
$crate::gen_statem!(@arms ($Ev) ($cur, ev) [ ]
|
||||||
$( on $st => { $($rows)* } )+);
|
$( on $st => { $($rows)* } )+);
|
||||||
match next {
|
match next {
|
||||||
$crate::statem::Resolution::To(s) if s == $cur => {}
|
$crate::gen_statem::Resolution::To(s) if s == $cur => {}
|
||||||
$crate::statem::Resolution::To(s) => {
|
$crate::gen_statem::Resolution::To(s) => {
|
||||||
self.state = s; // <- sole writer of the state cell
|
self.state = s; // <- sole writer of the state cell
|
||||||
self.enter(s, $cx);
|
self.enter(s, $cx);
|
||||||
}
|
}
|
||||||
$crate::statem::Resolution::Postpone => {
|
$crate::gen_statem::Resolution::Postpone => {
|
||||||
unreachable!("postpone is unreachable until chunk 3")
|
unreachable!("postpone is unreachable until chunk 3")
|
||||||
}
|
}
|
||||||
$crate::statem::Resolution::Unhandled => $cx.on_unhandled(),
|
$crate::gen_statem::Resolution::Unhandled => $cx.on_unhandled(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,7 +493,7 @@ macro_rules! gen_statem {
|
|||||||
{ cast $ev:pat $(if $g:expr)? => unhandled , $($rows:tt)* } { $($more:tt)* }
|
{ cast $ev:pat $(if $g:expr)? => unhandled , $($rows:tt)* } { $($more:tt)* }
|
||||||
) => {
|
) => {
|
||||||
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
||||||
[ $($arms)* ($st, $Ev::Cast($ev)) $(if $g)? => $crate::statem::Resolution::Unhandled, ]
|
[ $($arms)* ($st, $Ev::Cast($ev)) $(if $g)? => $crate::gen_statem::Resolution::Unhandled, ]
|
||||||
($st) { $($rows)* } { $($more)* })
|
($st) { $($rows)* } { $($more)* })
|
||||||
};
|
};
|
||||||
// cast, transition / stay / branch
|
// cast, transition / stay / branch
|
||||||
@@ -501,7 +501,7 @@ macro_rules! gen_statem {
|
|||||||
{ cast $ev:pat $(if $g:expr)? => $tail:expr , $($rows:tt)* } { $($more:tt)* }
|
{ cast $ev:pat $(if $g:expr)? => $tail:expr , $($rows:tt)* } { $($more:tt)* }
|
||||||
) => {
|
) => {
|
||||||
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
||||||
[ $($arms)* ($st, $Ev::Cast($ev)) $(if $g)? => $crate::statem::Resolution::To($tail.into()), ]
|
[ $($arms)* ($st, $Ev::Cast($ev)) $(if $g)? => $crate::gen_statem::Resolution::To($tail.into()), ]
|
||||||
($st) { $($rows)* } { $($more)* })
|
($st) { $($rows)* } { $($more)* })
|
||||||
};
|
};
|
||||||
// call, explicit refusal
|
// call, explicit refusal
|
||||||
@@ -509,7 +509,7 @@ macro_rules! gen_statem {
|
|||||||
{ call $ev:pat $(if $g:expr)? => unhandled , $($rows:tt)* } { $($more:tt)* }
|
{ call $ev:pat $(if $g:expr)? => unhandled , $($rows:tt)* } { $($more:tt)* }
|
||||||
) => {
|
) => {
|
||||||
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
||||||
[ $($arms)* ($st, $Ev::Call($ev)) $(if $g)? => $crate::statem::Resolution::Unhandled, ]
|
[ $($arms)* ($st, $Ev::Call($ev)) $(if $g)? => $crate::gen_statem::Resolution::Unhandled, ]
|
||||||
($st) { $($rows)* } { $($more)* })
|
($st) { $($rows)* } { $($more)* })
|
||||||
};
|
};
|
||||||
// call, transition / stay / branch
|
// call, transition / stay / branch
|
||||||
@@ -517,7 +517,7 @@ macro_rules! gen_statem {
|
|||||||
{ call $ev:pat $(if $g:expr)? => $tail:expr , $($rows:tt)* } { $($more:tt)* }
|
{ call $ev:pat $(if $g:expr)? => $tail:expr , $($rows:tt)* } { $($more:tt)* }
|
||||||
) => {
|
) => {
|
||||||
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
$crate::gen_statem!(@rows ($Ev) ($ss, $se)
|
||||||
[ $($arms)* ($st, $Ev::Call($ev)) $(if $g)? => $crate::statem::Resolution::To($tail.into()), ]
|
[ $($arms)* ($st, $Ev::Call($ev)) $(if $g)? => $crate::gen_statem::Resolution::To($tail.into()), ]
|
||||||
($st) { $($rows)* } { $($more)* })
|
($st) { $($rows)* } { $($more)* })
|
||||||
};
|
};
|
||||||
// this block is drained: hand the remaining on-blocks back to @arms
|
// this block is drained: hand the remaining on-blocks back to @arms
|
||||||
@@ -530,7 +530,7 @@ macro_rules! gen_statem {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod gen_statem_tests {
|
mod gen_statem_tests {
|
||||||
use crate::statem::Reply;
|
use crate::gen_statem::Reply;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
enum Switch {
|
enum Switch {
|
||||||
+2
-2
@@ -27,7 +27,7 @@ pub mod registry;
|
|||||||
pub mod pg;
|
pub mod pg;
|
||||||
pub mod link;
|
pub mod link;
|
||||||
pub mod gen_server;
|
pub mod gen_server;
|
||||||
pub mod statem;
|
pub mod gen_statem;
|
||||||
pub mod introspect;
|
pub mod introspect;
|
||||||
#[cfg(feature = "observer")]
|
#[cfg(feature = "observer")]
|
||||||
pub mod observer;
|
pub mod observer;
|
||||||
@@ -58,7 +58,7 @@ pub use gen_server::{
|
|||||||
call, cast, shutdown, whereis_server, CallError, CallTimeoutError, CastError, GenServer,
|
call, cast, shutdown, whereis_server, CallError, CallTimeoutError, CastError, GenServer,
|
||||||
NamedServerBuilder, ServerBuilder, ServerCtx, ServerName, ServerRef, TimerHandle, Watcher,
|
NamedServerBuilder, ServerBuilder, ServerCtx, ServerName, ServerRef, TimerHandle, Watcher,
|
||||||
};
|
};
|
||||||
pub use statem::{
|
pub use gen_statem::{
|
||||||
CallError as StatemCallError, Cx, Machine, Reply, Resolution, SendError as StatemSendError,
|
CallError as StatemCallError, Cx, Machine, Reply, Resolution, SendError as StatemSendError,
|
||||||
StatemRef,
|
StatemRef,
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
//! hand-written machine, `enter` firing on start and on every real transition
|
//! hand-written machine, `enter` firing on start and on every real transition
|
||||||
//! (but not on a stay), and the machine-down path when a handler panics.
|
//! (but not on a stay), and the machine-down path when a handler panics.
|
||||||
//!
|
//!
|
||||||
//! These drive the `smarm::statem` primitives directly, the same way a
|
//! These drive the `smarm::gen_statem` primitives directly, the same way a
|
||||||
//! `statem!`-generated machine eventually will.
|
//! `statem!`-generated machine eventually will.
|
||||||
|
|
||||||
use smarm::run;
|
use smarm::run;
|
||||||
use smarm::statem::{self, CallError, Cx, Machine, Reply, Resolution, StatemRef};
|
use smarm::gen_statem::{self, CallError, Cx, Machine, Reply, Resolution, StatemRef};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -46,7 +46,7 @@ struct Sm {
|
|||||||
|
|
||||||
impl Sm {
|
impl Sm {
|
||||||
fn start(init: Switch) -> StatemRef<Sm> {
|
fn start(init: Switch) -> StatemRef<Sm> {
|
||||||
statem::spawn(Sm { state: init, data: Counts { flips: 0, enters: 0 } })
|
gen_statem::spawn(Sm { state: init, data: Counts { flips: 0, enters: 0 } })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter(&mut self, _s: Switch, _cx: &mut Cx<Ev>) {
|
fn enter(&mut self, _s: Switch, _cx: &mut Cx<Ev>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user