Skip to main content

sync_decision/
sync_decision.rs

1//! Rewrite a candidate while the caller holds a std Mutex, then commit the
2//! checked result. Run with `cargo run -p rutis --example sync_decision`.
3
4use std::sync::Mutex;
5
6use rutis::{CordisError, Ctx, Event, EventKey, SyncEvent, SyncNext};
7
8struct BeforeSave {
9    candidate: u64,
10}
11impl Event for BeforeSave {
12    const NAME: &'static str = "example::BeforeSave";
13    type Value = u64;
14}
15impl SyncEvent for BeforeSave {}
16const BEFORE_SAVE: EventKey<BeforeSave> = EventKey::named("before-save");
17
18fn save(ctx: &Ctx, state: &Mutex<u64>, candidate: u64) -> Result<u64, CordisError> {
19    let mut current = state.lock().unwrap();
20    let proposed =
21        ctx.events()
22            .waterfall_sync(ctx, &BEFORE_SAVE, &BeforeSave { candidate }, |_, event| {
23                Ok((*current).max(event.candidate))
24            })?;
25    // Middleware only proposes a value; the caller validates before writing.
26    if proposed < *current {
27        return Err(CordisError::Validation {
28            issues: vec!["saved value must not decrease".into()],
29        });
30    }
31    *current = proposed;
32    Ok(proposed)
33}
34
35#[tokio::main]
36async fn main() -> Result<(), Box<dyn std::error::Error>> {
37    let root = Ctx::root()?;
38    root.events().on_waterfall_sync(
39        &root,
40        &BEFORE_SAVE,
41        |_: &Ctx, _: &BeforeSave, next: SyncNext<'_, BeforeSave>| {
42            // Fixed input: rewrite the downstream result as it returns.
43            Ok(next.call()?.min(100))
44        },
45    )?;
46    let state = Mutex::new(10);
47    assert_eq!(save(&root, &state, 120)?, 100);
48    println!("saved {}", state.lock().unwrap());
49    root.shutdown().await?;
50    Ok(())
51}