rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_core::WindowId;
use rmux_proto::WindowTarget;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct AppliedWindowResize {
    target: WindowTarget,
    window_id: Option<WindowId>,
    layout_change_prepared: bool,
}

impl AppliedWindowResize {
    pub(crate) fn into_parts(self) -> (WindowTarget, bool) {
        (self.target, self.layout_change_prepared)
    }

    fn matches(&self, target: &WindowTarget, window_id: Option<WindowId>) -> bool {
        self.target == *target
            || self
                .window_id
                .zip(window_id)
                .is_some_and(|(pending, candidate)| pending == candidate)
    }
}

#[derive(Debug, Clone, Default)]
pub(crate) struct AppliedWindowResizeQueue {
    pending: Vec<AppliedWindowResize>,
}

impl AppliedWindowResizeQueue {
    pub(crate) fn record(&mut self, target: WindowTarget, window_id: Option<WindowId>) {
        if self
            .pending
            .iter()
            .any(|resize| resize.matches(&target, window_id))
        {
            return;
        }
        self.pending.push(AppliedWindowResize {
            target,
            window_id,
            layout_change_prepared: false,
        });
    }

    pub(crate) fn claim_layout_change(
        &mut self,
        target: &WindowTarget,
        window_id: Option<WindowId>,
    ) {
        if let Some(resize) = self
            .pending
            .iter_mut()
            .find(|resize| resize.matches(target, window_id))
        {
            resize.layout_change_prepared = true;
        }
    }

    pub(crate) fn take(&mut self) -> Vec<AppliedWindowResize> {
        std::mem::take(&mut self.pending)
    }
}

#[cfg(test)]
mod tests {
    use rmux_core::WindowId;
    use rmux_proto::{SessionName, WindowTarget};

    use super::AppliedWindowResizeQueue;

    #[test]
    fn linked_alias_claims_the_pending_layout_producer_by_window_identity() {
        let mut queue = AppliedWindowResizeQueue::default();
        let source = target("source", 0);
        let alias = target("alias", 7);
        let window_id = WindowId::new(42);
        queue.record(source, Some(window_id));

        queue.claim_layout_change(&alias, Some(window_id));

        let pending = queue.take();
        assert_eq!(pending.len(), 1);
        let (_, layout_change_prepared) = pending
            .into_iter()
            .next()
            .expect("recorded resize")
            .into_parts();
        assert!(layout_change_prepared);
    }

    #[test]
    fn unrelated_layout_event_does_not_claim_the_pending_resize() {
        let mut queue = AppliedWindowResizeQueue::default();
        queue.record(target("source", 0), Some(WindowId::new(42)));

        queue.claim_layout_change(&target("other", 0), Some(WindowId::new(43)));

        let (_, layout_change_prepared) = queue
            .take()
            .into_iter()
            .next()
            .expect("recorded resize")
            .into_parts();
        assert!(!layout_change_prepared);
    }

    fn target(session: &str, window_index: u32) -> WindowTarget {
        WindowTarget::with_window(
            SessionName::new(session).expect("test session name"),
            window_index,
        )
    }
}