rust-elm 0.1.0

Elm Architecture for Rust: composable reducers, pure effects, async runtime.
Documentation
use rust_elm::{
    allow_state_clones, shared_get, shared_with_mut, Cmd, InMemoryStorage, ReplayHarness, Shared,
    panic_on_state_clone,
};

panic_on_state_clone! {
    #[derive(Debug, PartialEq, Eq, Default)]
    struct AppState {
        count: i32,
    }
}

struct ScopedView {
    shared: Shared<AppState>,
}

fn update(state: &mut AppState, msg: i32) -> Cmd<i32> {
    state.count += msg;
    Cmd::none()
}

#[test]
fn shared_value_visible_across_scopes() {
    let shared = Shared::new(AppState::default());
    let parent = shared.clone();
    let child = ScopedView {
        shared: shared.clone(),
    };

    shared_with_mut(&parent, |s| {
        s.count = 5;
    });
    assert_eq!(shared_get(&child.shared).count, 5);

    shared_with_mut(&child.shared, |s| {
        s.count += 2;
    });
    assert_eq!(shared_get(&parent).count, 7);
}

#[test]
fn shared_persist_and_replay_restore() {
    let storage = InMemoryStorage::new();
    let shared = Shared::new(AppState { count: 10 });
    allow_state_clones(2, || shared.persist(&storage, "app")).unwrap();

    let mut harness = ReplayHarness::new(AppState::default(), update);
    harness.send(3);
    assert_eq!(harness.state.count, 3);

    let restored =
        allow_state_clones(1, || Shared::load(&storage, "app", AppState::default())).unwrap();
    harness.restore(shared_get(&restored));
    assert_eq!(harness.state.count, 10);

    harness.send(1);
    assert_eq!(harness.state.count, 11);
}

#[cfg(feature = "serde")]
#[test]
fn file_storage_round_trip_integration() {
    use rust_elm::FileStorage;

    panic_on_state_clone! {
        #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
        struct Persisted {
            count: i32,
        }
    }

    let dir = std::env::temp_dir().join(format!("rust_elm_shared_int_{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&dir);
    let storage = FileStorage::new(&dir);
    let shared = Shared::new(Persisted { count: 99 });
    allow_state_clones(2, || shared.persist(&storage, "app")).unwrap();
    let loaded =
        allow_state_clones(1, || Shared::load(&storage, "app", Persisted { count: 0 })).unwrap();
    assert_eq!(shared_get(&loaded).count, 99);
    let _ = std::fs::remove_dir_all(&dir);
}

#[cfg(feature = "serde")]
#[test]
fn replay_snapshot_record_round_trip() {
    use rust_elm::StateSnapshot;

    let mut harness = ReplayHarness::new(AppState::default(), update);
    harness.send(4);
    let snap = allow_state_clones(1, || harness.snapshot_record());
    harness.send(10);
    assert_eq!(harness.state.count, 14);

    allow_state_clones(1, || harness.restore_record(snap.clone()));
    assert_eq!(harness.state.count, 4);
    assert_eq!(snap, StateSnapshot {
        state: AppState { count: 4 }
    });
}