cranpose-core 0.1.141

Core runtime for a Jetpack Compose inspired UI framework in Rust
Documentation
use super::*;

#[derive(Clone, Copy)]
struct Probe {
    count: MutableState<i32>,
    label: MutableState<&'static str>,
}

impl Probe {
    fn new() -> Self {
        Probe {
            count: mutableStateOf(0),
            label: mutableStateOf("first"),
        }
    }
}

fn render_probe(
    composition: &mut Composition<MemoryApplier>,
    root_key: Key,
    builds: &Rc<Cell<usize>>,
    seen: &Rc<Cell<Option<Probe>>>,
    show: MutableState<bool>,
) {
    let builds = Rc::clone(builds);
    let seen = Rc::clone(seen);
    composition
        .render(root_key, move || {
            if show.value() {
                let probe = remember(|| {
                    builds.set(builds.get() + 1);
                    Probe::new()
                })
                .with(|probe| *probe);
                seen.set(Some(probe));
            }
        })
        .expect("render the state holder");
    assert_composition_valid(composition);
}

#[test]
fn a_remembered_holder_is_built_once_and_comes_back_the_same_object() {
    let mut composition = test_composition();
    let show = MutableState::with_runtime(true, composition.runtime_handle());
    let builds = Rc::new(Cell::new(0));
    let seen = Rc::new(Cell::new(None::<Probe>));
    let root_key = location_key(file!(), line!(), column!());

    render_probe(&mut composition, root_key, &builds, &seen, show);
    let first = seen.get().expect("the holder is built on the first pass");
    assert_eq!(builds.get(), 1, "the first pass builds the holder");
    first.count.set_value(7);
    first.label.set_value("second");

    render_probe(&mut composition, root_key, &builds, &seen, show);
    let again = seen.get().expect("the holder is there on the second pass");
    assert_eq!(builds.get(), 1, "a later pass must not build it again");
    assert_eq!(
        again.count, first.count,
        "the holder that comes back is the one that was built"
    );
    assert_eq!(again.label, first.label);
    assert_eq!(again.count.value(), 7, "its states keep their values");
    assert_eq!(again.label.value(), "second");
}

#[test]
fn a_remembered_holder_releases_its_states_when_it_leaves() {
    let mut composition = test_composition();
    let show = MutableState::with_runtime(true, composition.runtime_handle());
    let builds = Rc::new(Cell::new(0));
    let seen = Rc::new(Cell::new(None::<Probe>));
    let root_key = location_key(file!(), line!(), column!());

    render_probe(&mut composition, root_key, &builds, &seen, show);
    let probe = seen.get().expect("the holder is built on the first pass");
    assert!(probe.count.is_alive());
    assert!(probe.label.is_alive());

    show.set_value(false);
    render_probe(&mut composition, root_key, &builds, &seen, show);
    assert!(
        !probe.count.is_alive(),
        "the slot owns the states and lets them go with it"
    );
    assert!(!probe.label.is_alive());
}

#[test]
fn the_runtime_never_takes_ownership_of_a_remembered_holder_s_states() {
    let mut composition = test_composition();
    let runtime = composition.runtime_handle();
    let show = MutableState::with_runtime(true, runtime.clone());
    let builds = Rc::new(Cell::new(0));
    let seen = Rc::new(Cell::new(None::<Probe>));
    let root_key = location_key(file!(), line!(), column!());
    let owned_by_runtime = runtime.debug_stats().external_state_owners_len;

    for _ in 0..8 {
        render_probe(&mut composition, root_key, &builds, &seen, show);
    }

    assert_eq!(
        runtime.debug_stats().external_state_owners_len,
        owned_by_runtime,
        "a state made while a remember builds its value belongs to the slot, \
         not to the runtime; parking it on the runtime is what used to make \
         the Kotlin shape leak here"
    );
}