amethystate 0.23.2

A state manager for Rust applications
Documentation
use amethystate::store::builder::{Backend, StoreBuilder};
use amethystate::{ReactiveMap, amethystate};
use amethystate_core::test_utils::TempPath;
use amethystate_test_macros::backends;
use serde::{Deserialize, Serialize};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;

#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Limits {
    pub warning: u64,
}

#[amethystate(prefix = "dbg")]
pub struct Settings {
    #[amestate(default = 8080)]
    pub port: u16,

    #[amestate(default = "dark".to_string())]
    pub theme: String,

    #[amestate(default = { "cpu": Limits { warning: 70 } })]
    pub limits: ReactiveMap<String, Limits>,
}

/// Deliberately not `Debug`: the framework must not demand it.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Opaque {
    pub inner: u8,
}

#[amethystate(prefix = "dbg_opaque")]
pub struct HasOpaque {
    #[amestate(default = 1)]
    pub port: u16,

    #[amestate(default = Opaque { inner: 7 })]
    pub opaque: Opaque,
}

fn assert_debug<T: std::fmt::Debug>() {}

#[backends(all)]
fn a_field_type_need_not_be_printable(backend: Backend) {
    let at = TempPath::new("dbg_opaque");
    let store = StoreBuilder::new(&at).backend(backend).build().unwrap();
    let state = HasOpaque::new_with(&store).unwrap();

    assert_eq!(state.opaque().get().inner, 7);
    assert_eq!(state.port().get(), 1);
}

#[test]
fn a_printable_struct_still_gets_its_impl() {
    assert_debug::<Settings>();
}

fn settings(backend: Backend, tag: &str) -> (TempPath, Settings) {
    let at = TempPath::new(tag);
    let store = StoreBuilder::new(&at).backend(backend).build().unwrap();
    let held = Settings::new_with(&store).unwrap();
    (at, held)
}

#[backends(all)]
fn a_field_shows_its_path_and_value(backend: Backend) {
    let (_at, state) = settings(backend, "dbg_field");
    let shown = format!("{:?}", state.port());

    assert!(shown.contains("Field"), "{shown}");
    assert!(shown.contains("dbg.port"), "{shown}");
    assert!(shown.contains("8080"), "{shown}");
}

#[backends(all)]
fn a_field_shows_the_current_value_not_the_default(backend: Backend) {
    let (_at, state) = settings(backend, "dbg_current");
    state.port().set(9090).unwrap();

    let shown = format!("{:?}", state.port());
    assert!(shown.contains("9090"), "{shown}");
    assert!(!shown.contains("8080"), "{shown}");
}

#[backends(all)]
fn a_state_struct_shows_every_field_by_name(backend: Backend) {
    let (_at, state) = settings(backend, "dbg_struct");
    let shown = format!("{state:?}");

    assert!(shown.starts_with("Settings {"), "{shown}");
    for expected in ["port", "8080", "theme", "dark", "limits", "cpu", "70"] {
        assert!(shown.contains(expected), "missing {expected} in {shown}");
    }
}

#[backends(all)]
fn the_instance_id_stays_out_of_the_output(backend: Backend) {
    let (_at, state) = settings(backend, "dbg_no_id");
    let shown = format!("{state:?}");

    assert!(
        !shown.contains("__amethystate_instance_id"),
        "internals leaked: {shown}"
    );
}

#[amethystate(prefix = "dbg_p_opaque", mode = "persistent")]
pub struct PersistentOpaque {
    #[amestate(default = Opaque { inner: 3 })]
    pub opaque: Opaque,
}

#[derive(Debug)]
#[amethystate(prefix = "dbg_p_shown", mode = "persistent")]
pub struct PersistentShown {
    #[amestate(default = 5)]
    pub port: u16,
}

#[backends(all)]
fn a_persistent_struct_needs_no_debug_either(backend: Backend) {
    let at = TempPath::new("dbg_p_opaque");
    let store = StoreBuilder::new(&at).backend(backend).build().unwrap();
    let state = PersistentOpaque::load_with(&store).unwrap();

    assert_eq!(state.opaque.inner, 3);
}

#[backends(all)]
fn a_persistent_struct_prints_when_asked(backend: Backend) {
    let at = TempPath::new("dbg_p_shown");
    let store = StoreBuilder::new(&at).backend(backend).build().unwrap();
    let state = PersistentShown::load_with(&store).unwrap();

    let shown = format!("{state:?}");
    assert!(shown.contains("PersistentShown"), "{shown}");
    assert!(shown.contains("5"), "{shown}");
}