windows-reactor 0.100.0

Declarative Windows UI library for Rust
Documentation
use rustc_hash::FxHashMap;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;

use super::*;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RealizationLease {
    pub identity: WindowToken,
    pub collection: NodeId,
    pub container: RealizedContainer,
    pub key: Key,
    pub revision: u64,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RealizedContainer(pub u64);

#[derive(Clone)]
pub struct VirtualModel {
    active: HashMap<Key, (u64, RealizedContainer)>,
    collection: NodeId,
    containers: FxHashMap<RealizedContainer, (Key, u64)>,
    identity: WindowToken,
    keys: Rc<Vec<Key>>,
    revision: u64,
    source_revision: u64,
}

impl VirtualModel {
    pub fn new(
        identity: WindowToken,
        collection: NodeId,
        keys: impl IntoIterator<Item = Key>,
    ) -> Result<Self, DuplicateKeyError<Key>> {
        let keys = keys.into_iter().collect::<Vec<_>>();
        diff(&[], &keys)?;
        Ok(Self {
            active: HashMap::new(),
            collection,
            containers: FxHashMap::default(),
            identity,
            keys: Rc::new(keys),
            revision: 0,
            source_revision: 0,
        })
    }

    pub fn update(
        &mut self,
        keys: impl IntoIterator<Item = Key>,
    ) -> Result<Vec<KeyedOperation<Key>>, DuplicateKeyError<Key>> {
        let keys = keys.into_iter().collect::<Vec<_>>();
        let operations = diff(&self.keys, &keys)?;
        let source_revision = self.source_revision.checked_add(1).unwrap();
        let retained = keys.iter().cloned().collect::<HashSet<_>>();
        self.active.retain(|key, _| retained.contains(key));
        self.containers.retain(|_, (key, _)| retained.contains(key));
        self.keys = Rc::new(keys);
        self.source_revision = source_revision;
        Ok(operations)
    }

    pub fn realize(
        &mut self,
        index: usize,
        container: RealizedContainer,
    ) -> Option<RealizationLease> {
        let key = self.keys.get(index).cloned()?;
        self.revision = self.revision.checked_add(1).unwrap();
        if let Some((old_key, old_revision)) = self.containers.remove(&container)
            && self.active.get(&old_key) == Some(&(old_revision, container))
        {
            self.active.remove(&old_key);
        }
        if let Some((_, old_container)) = self.active.remove(&key) {
            self.containers.remove(&old_container);
        }
        self.active.insert(key.clone(), (self.revision, container));
        self.containers
            .insert(container, (key.clone(), self.revision));
        Some(RealizationLease {
            identity: self.identity,
            collection: self.collection,
            container,
            key,
            revision: self.revision,
        })
    }

    #[cfg(test)]
    pub fn accepts(&self, lease: &RealizationLease) -> bool {
        lease.identity == self.identity
            && lease.collection == self.collection
            && self.active.get(&lease.key) == Some(&(lease.revision, lease.container))
    }

    #[cfg(test)]
    pub fn recycle(&mut self, lease: &RealizationLease) -> bool {
        if !self.accepts(lease) {
            return false;
        }
        self.active.remove(&lease.key);
        self.containers.remove(&lease.container);
        true
    }

    pub fn recycle_container(&mut self, container: RealizedContainer) -> Option<RealizationLease> {
        let (key, revision) = self.containers.remove(&container)?;
        if self.active.get(&key) != Some(&(revision, container)) {
            return None;
        }
        self.active.remove(&key);
        Some(RealizationLease {
            identity: self.identity,
            collection: self.collection,
            container,
            key,
            revision,
        })
    }

    pub fn clear(&mut self) {
        self.active.clear();
        self.containers.clear();
    }

    #[cfg(test)]
    pub fn active_len(&self) -> usize {
        self.active.len()
    }

    pub fn keys(&self) -> &[Key] {
        &self.keys
    }

    pub fn source_revision(&self) -> u64 {
        self.source_revision
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const COLLECTION: NodeId = NodeId::from_parts(4, 2);
    const FIRST: RealizedContainer = RealizedContainer(10);
    const SECOND: RealizedContainer = RealizedContainer(11);

    fn identity() -> WindowToken {
        WindowToken::new(WindowId::allocate())
    }

    fn keys(values: &[&str]) -> Vec<Key> {
        values.iter().copied().map(Key::from).collect()
    }

    #[test]
    fn update_uses_shared_keyed_operations_and_retains_moved_lease() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a", "b", "c"])).unwrap();
        let lease = model.realize(1, FIRST).unwrap();

        let operations = model.update(keys(&["c", "b", "d"])).unwrap();

        assert_eq!(model.keys(), keys(&["c", "b", "d"]));
        assert!(operations
            .iter()
            .any(|operation| matches!(operation, KeyedOperation::Remove { key } if key == &Key::from("a"))));
        assert!(operations
            .iter()
            .any(|operation| matches!(operation, KeyedOperation::Insert { key, .. } if key == &Key::from("d"))));
        assert!(model.accepts(&lease));
    }

    #[test]
    fn removed_and_recycled_leases_are_rejected() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a", "b"])).unwrap();
        let removed = model.realize(0, FIRST).unwrap();
        let recycled = model.realize(1, SECOND).unwrap();

        model.update(keys(&["b"])).unwrap();

        assert!(!model.accepts(&removed));
        assert!(model.recycle(&recycled));
        assert!(!model.accepts(&recycled));
        assert!(!model.recycle(&recycled));
    }

    #[test]
    fn replacement_realization_invalidates_older_callback() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a"])).unwrap();
        let first = model.realize(0, FIRST).unwrap();
        let second = model.realize(0, SECOND).unwrap();

        assert!(!model.accepts(&first));
        assert!(model.accepts(&second));
        assert_eq!(model.recycle_container(FIRST), None);
        assert_eq!(model.recycle_container(SECOND), Some(second));
    }

    #[test]
    fn collection_identity_rejects_foreign_lease() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a"])).unwrap();
        let lease = model.realize(0, FIRST).unwrap();
        let foreign = RealizationLease {
            collection: NodeId::from_parts(5, 2),
            ..lease
        };

        assert!(!model.accepts(&foreign));
    }

    #[test]
    fn reused_container_invalidates_its_previous_key() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a", "b"])).unwrap();
        let first = model.realize(0, FIRST).unwrap();
        let second = model.realize(1, FIRST).unwrap();

        assert!(!model.accepts(&first));
        assert!(model.accepts(&second));
        assert_eq!(model.active_len(), 1);
        assert_eq!(model.recycle_container(FIRST), Some(second));
    }

    #[test]
    fn repeated_realize_recycle_returns_to_zero_resources() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a", "b", "c"])).unwrap();

        for _ in 0..10_000 {
            for index in 0..model.keys().len() {
                let lease = model
                    .realize(index, RealizedContainer(index as u64))
                    .unwrap();
                assert!(model.recycle(&lease));
            }
            assert_eq!(model.active_len(), 0);
        }

        model.clear();
        assert_eq!(model.active_len(), 0);
    }

    #[test]
    fn rejects_duplicate_keys_and_out_of_range_realizations() {
        assert_eq!(
            VirtualModel::new(identity(), COLLECTION, keys(&["a", "a"])).err(),
            Some(DuplicateKeyError(Key::from("a")))
        );
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a"])).unwrap();
        assert_eq!(model.realize(1, FIRST), None);
        assert_eq!(
            model.update(keys(&["a", "a"])),
            Err(DuplicateKeyError(Key::from("a")))
        );
    }

    #[test]
    fn source_revision_exhaustion_panics_before_update() {
        let mut model = VirtualModel::new(identity(), COLLECTION, keys(&["a"])).unwrap();
        model.source_revision = u64::MAX;

        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            _ = model.update(keys(&["b"]));
        }));

        assert!(result.is_err());
        assert_eq!(model.keys(), keys(&["a"]));
        assert_eq!(model.source_revision(), u64::MAX);
    }
}