Files
coop/.agents/skills/gpui-entity/references/advanced.md
Ren Amamiya 40d726c986 feat: refactor to use gpui event instead of local state (#18)
Reviewed-on: #18
Co-authored-by: Ren Amamiya <reya@lume.nu>
Co-committed-by: Ren Amamiya <reya@lume.nu>
2026-03-10 08:19:02 +00:00

12 KiB

Advanced Entity Patterns

Advanced techniques for sophisticated entity management scenarios.

Entity Collections Management

Dynamic Collection with Cleanup

struct EntityCollection<T> {
    strong_refs: Vec<Entity<T>>,
    weak_refs: Vec<WeakEntity<T>>,
}

impl<T> EntityCollection<T> {
    fn new() -> Self {
        Self {
            strong_refs: Vec::new(),
            weak_refs: Vec::new(),
        }
    }

    fn add(&mut self, entity: Entity<T>, cx: &mut App) {
        self.strong_refs.push(entity.clone());
        self.weak_refs.push(entity.downgrade());
    }

    fn remove(&mut self, entity_id: EntityId, cx: &mut App) {
        self.strong_refs.retain(|e| e.entity_id() != entity_id);
        self.weak_refs.retain(|w| {
            w.upgrade()
                .map(|e| e.entity_id() != entity_id)
                .unwrap_or(false)
        });
    }

    fn cleanup_invalid(&mut self, cx: &mut App) {
        self.weak_refs.retain(|weak| weak.upgrade().is_some());
    }

    fn for_each<F>(&self, cx: &mut App, mut f: F)
    where
        F: FnMut(&Entity<T>, &mut App),
    {
        for entity in &self.strong_refs {
            f(entity, cx);
        }
    }

    fn for_each_weak<F>(&mut self, cx: &mut App, mut f: F)
    where
        F: FnMut(Entity<T>, &mut App),
    {
        self.weak_refs.retain(|weak| {
            if let Some(entity) = weak.upgrade() {
                f(entity, cx);
                true
            } else {
                false // Remove invalid weak references
            }
        });
    }
}

Entity Registry Pattern

use std::collections::HashMap;

struct EntityRegistry<T> {
    entities: HashMap<EntityId, WeakEntity<T>>,
}

impl<T> EntityRegistry<T> {
    fn new() -> Self {
        Self {
            entities: HashMap::new(),
        }
    }

    fn register(&mut self, entity: &Entity<T>) {
        self.entities.insert(entity.entity_id(), entity.downgrade());
    }

    fn unregister(&mut self, entity_id: EntityId) {
        self.entities.remove(&entity_id);
    }

    fn get(&self, entity_id: EntityId) -> Option<Entity<T>> {
        self.entities.get(&entity_id)?.upgrade()
    }

    fn cleanup(&mut self) {
        self.entities.retain(|_, weak| weak.upgrade().is_some());
    }

    fn count(&self) -> usize {
        self.entities.len()
    }

    fn all_entities(&self) -> Vec<Entity<T>> {
        self.entities
            .values()
            .filter_map(|weak| weak.upgrade())
            .collect()
    }
}

Conditional Update Patterns

Debounced Updates

use std::time::{Duration, Instant};

struct DebouncedEntity<T> {
    entity: Entity<T>,
    last_update: Instant,
    debounce_duration: Duration,
    pending_update: Option<Box<dyn FnOnce(&mut T, &mut Context<T>)>>,
}

impl<T: 'static> DebouncedEntity<T> {
    fn new(entity: Entity<T>, debounce_ms: u64) -> Self {
        Self {
            entity,
            last_update: Instant::now(),
            debounce_duration: Duration::from_millis(debounce_ms),
            pending_update: None,
        }
    }

    fn update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: FnOnce(&mut T, &mut Context<T>) + 'static,
    {
        let now = Instant::now();
        let elapsed = now.duration_since(self.last_update);

        if elapsed >= self.debounce_duration {
            // Execute immediately
            self.entity.update(cx, update_fn);
            self.last_update = now;
            self.pending_update = None;
        } else {
            // Store for later
            self.pending_update = Some(Box::new(update_fn));

            // Schedule execution
            let entity = self.entity.clone();
            let delay = self.debounce_duration - elapsed;

            cx.spawn(async move |cx| {
                tokio::time::sleep(delay).await;

                if let Some(update) = self.pending_update.take() {
                    entity.update(cx, |state, inner_cx| {
                        update(state, inner_cx);
                    });
                }
            }).detach();
        }
    }
}

Throttled Updates

struct ThrottledEntity<T> {
    entity: Entity<T>,
    last_update: Instant,
    throttle_duration: Duration,
}

impl<T: 'static> ThrottledEntity<T> {
    fn new(entity: Entity<T>, throttle_ms: u64) -> Self {
        Self {
            entity,
            last_update: Instant::now(),
            throttle_duration: Duration::from_millis(throttle_ms),
        }
    }

    fn try_update<F>(&mut self, cx: &mut App, update_fn: F) -> bool
    where
        F: FnOnce(&mut T, &mut Context<T>),
    {
        let now = Instant::now();
        let elapsed = now.duration_since(self.last_update);

        if elapsed >= self.throttle_duration {
            self.entity.update(cx, update_fn);
            self.last_update = now;
            true
        } else {
            false // Update throttled
        }
    }
}

Entity State Machine Pattern

enum AppState {
    Idle,
    Loading,
    Loaded(String),
    Error(String),
}

struct StateMachine {
    state: AppState,
}

impl StateMachine {
    fn new() -> Self {
        Self {
            state: AppState::Idle,
        }
    }

    fn start_loading(&mut self, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Idle | AppState::Error(_)) {
            self.state = AppState::Loading;
            cx.notify();

            let weak_entity = cx.entity().downgrade();

            cx.spawn(async move |cx| {
                let result = perform_load().await;

                let _ = weak_entity.update(cx, |state, cx| {
                    match result {
                        Ok(data) => state.on_load_success(data, cx),
                        Err(e) => state.on_load_error(e.to_string(), cx),
                    }
                });
            }).detach();
        }
    }

    fn on_load_success(&mut self, data: String, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Loading) {
            self.state = AppState::Loaded(data);
            cx.notify();
        }
    }

    fn on_load_error(&mut self, error: String, cx: &mut Context<Self>) {
        if matches!(self.state, AppState::Loading) {
            self.state = AppState::Error(error);
            cx.notify();
        }
    }

    fn reset(&mut self, cx: &mut Context<Self>) {
        self.state = AppState::Idle;
        cx.notify();
    }
}

async fn perform_load() -> Result<String, anyhow::Error> {
    // Actual load implementation
    Ok("Data".to_string())
}

Entity Proxy Pattern

struct EntityProxy<T> {
    entity: WeakEntity<T>,
}

impl<T> EntityProxy<T> {
    fn new(entity: &Entity<T>) -> Self {
        Self {
            entity: entity.downgrade(),
        }
    }

    fn with<F, R>(&self, cx: &mut App, f: F) -> Result<R, anyhow::Error>
    where
        F: FnOnce(&T, &App) -> R,
    {
        self.entity.read_with(cx, f)
    }

    fn update<F, R>(&self, cx: &mut App, f: F) -> Result<R, anyhow::Error>
    where
        F: FnOnce(&mut T, &mut Context<T>) -> R,
    {
        self.entity.update(cx, f)
    }

    fn is_valid(&self, cx: &App) -> bool {
        self.entity.upgrade().is_some()
    }
}

Cascading Updates Pattern

struct CascadingUpdater {
    entities: Vec<WeakEntity<UpdateTarget>>,
}

impl CascadingUpdater {
    fn new() -> Self {
        Self {
            entities: Vec::new(),
        }
    }

    fn add_target(&mut self, entity: &Entity<UpdateTarget>) {
        self.entities.push(entity.downgrade());
    }

    fn cascade_update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: Fn(&mut UpdateTarget, &mut Context<UpdateTarget>) + Clone,
    {
        // Update all entities in sequence
        self.entities.retain(|weak| {
            if let Ok(_) = weak.update(cx, |state, inner_cx| {
                update_fn.clone()(state, inner_cx);
            }) {
                true // Keep valid entity
            } else {
                false // Remove invalid entity
            }
        });
    }
}

struct UpdateTarget {
    value: i32,
}

Entity Snapshot Pattern

use serde::{Serialize, Deserialize};

#[derive(Clone, Serialize, Deserialize)]
struct EntitySnapshot {
    data: String,
    timestamp: u64,
}

struct SnapshotableEntity {
    data: String,
    snapshots: Vec<EntitySnapshot>,
}

impl SnapshotableEntity {
    fn new(data: String) -> Self {
        Self {
            data,
            snapshots: Vec::new(),
        }
    }

    fn take_snapshot(&mut self, cx: &mut Context<Self>) {
        let snapshot = EntitySnapshot {
            data: self.data.clone(),
            timestamp: current_timestamp(),
        };
        self.snapshots.push(snapshot);
        cx.notify();
    }

    fn restore_snapshot(&mut self, index: usize, cx: &mut Context<Self>) -> Result<(), String> {
        if let Some(snapshot) = self.snapshots.get(index) {
            self.data = snapshot.data.clone();
            cx.notify();
            Ok(())
        } else {
            Err("Invalid snapshot index".to_string())
        }
    }

    fn clear_old_snapshots(&mut self, keep_last: usize, cx: &mut Context<Self>) {
        if self.snapshots.len() > keep_last {
            self.snapshots.drain(0..self.snapshots.len() - keep_last);
            cx.notify();
        }
    }
}

fn current_timestamp() -> u64 {
    use std::time::{SystemTime, UNIX_EPOCH};
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs()
}

Entity Transaction Pattern

struct Transaction<T> {
    entity: Entity<T>,
    original_state: Option<T>,
}

impl<T: Clone> Transaction<T> {
    fn begin(entity: Entity<T>, cx: &mut App) -> Self {
        let original_state = entity.read(cx).clone();

        Self {
            entity,
            original_state: Some(original_state),
        }
    }

    fn update<F>(&mut self, cx: &mut App, update_fn: F)
    where
        F: FnOnce(&mut T, &mut Context<T>),
    {
        self.entity.update(cx, update_fn);
    }

    fn commit(mut self, cx: &mut App) {
        self.original_state = None; // Don't rollback
        self.entity.update(cx, |_, cx| {
            cx.notify();
        });
    }

    fn rollback(mut self, cx: &mut App) {
        if let Some(original) = self.original_state.take() {
            self.entity.update(cx, |state, cx| {
                *state = original;
                cx.notify();
            });
        }
    }
}

impl<T> Drop for Transaction<T> {
    fn drop(&mut self) {
        // Auto-rollback if not committed
        if self.original_state.is_some() {
            eprintln!("Warning: Transaction dropped without commit");
        }
    }
}

// Usage
fn perform_transaction(entity: Entity<MyState>, cx: &mut App) -> Result<(), String> {
    let mut tx = Transaction::begin(entity, cx);

    tx.update(cx, |state, cx| {
        state.value = 42;
    });

    if validate_state(&tx.entity, cx)? {
        tx.commit(cx);
        Ok(())
    } else {
        tx.rollback(cx);
        Err("Validation failed".to_string())
    }
}

Entity Pool Pattern

struct EntityPool<T> {
    available: Vec<Entity<T>>,
    in_use: Vec<WeakEntity<T>>,
    factory: Box<dyn Fn(&mut App) -> Entity<T>>,
}

impl<T: 'static> EntityPool<T> {
    fn new<F>(factory: F) -> Self
    where
        F: Fn(&mut App) -> Entity<T> + 'static,
    {
        Self {
            available: Vec::new(),
            in_use: Vec::new(),
            factory: Box::new(factory),
        }
    }

    fn acquire(&mut self, cx: &mut App) -> Entity<T> {
        let entity = if let Some(entity) = self.available.pop() {
            entity
        } else {
            (self.factory)(cx)
        };

        self.in_use.push(entity.downgrade());
        entity
    }

    fn release(&mut self, entity: Entity<T>, cx: &mut App) {
        // Reset entity state if needed
        entity.update(cx, |state, cx| {
            // Reset logic here
            cx.notify();
        });

        self.available.push(entity);
        self.cleanup_in_use();
    }

    fn cleanup_in_use(&mut self) {
        self.in_use.retain(|weak| weak.upgrade().is_some());
    }

    fn pool_size(&self) -> (usize, usize) {
        (self.available.len(), self.in_use.len())
    }
}

These advanced patterns provide powerful abstractions for managing complex entity scenarios while maintaining code quality and performance.