rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
#[derive(Debug, Clone, Copy)]
pub(super) struct ControlCommandFrame {
    pub(super) number: u64,
    pub(super) guard_flag: u8,
    pub(super) origin: ControlCommandOrigin,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum ControlCommandOrigin {
    Initial { completes_batch: bool },
    Stdin,
}

impl ControlCommandOrigin {
    pub(super) const fn completes_initial_batch(self) -> bool {
        matches!(
            self,
            Self::Initial {
                completes_batch: true
            }
        )
    }
}

#[derive(Debug)]
pub(super) struct ControlCommandNumbering {
    next_number: u64,
    initial_frames_remaining: usize,
}

impl ControlCommandNumbering {
    pub(super) fn after_initial_ack() -> Self {
        Self {
            next_number: 2,
            initial_frames_remaining: 0,
        }
    }

    pub(super) fn with_initial_commands(command_count: usize) -> Self {
        Self {
            next_number: 1,
            initial_frames_remaining: command_count,
        }
    }

    pub(super) fn next_frame(&mut self) -> ControlCommandFrame {
        let number = self.next_number;
        self.next_number = self.next_number.saturating_add(1);
        let origin = match self.initial_frames_remaining {
            0 => ControlCommandOrigin::Stdin,
            1 => {
                self.initial_frames_remaining = 0;
                ControlCommandOrigin::Initial {
                    completes_batch: true,
                }
            }
            _ => {
                self.initial_frames_remaining -= 1;
                ControlCommandOrigin::Initial {
                    completes_batch: false,
                }
            }
        };
        let guard_flag = match origin {
            ControlCommandOrigin::Initial { .. } => 0,
            ControlCommandOrigin::Stdin => 1,
        };
        ControlCommandFrame {
            number,
            guard_flag,
            origin,
        }
    }

    pub(super) fn next_synchronous_child_number(&mut self) -> u64 {
        let number = self.next_number;
        self.next_number = self.next_number.saturating_add(1);
        number
    }
}

#[cfg(test)]
mod tests {
    use super::{ControlCommandNumbering, ControlCommandOrigin};

    #[test]
    fn initial_batch_boundary_is_explicit_and_stdin_frames_follow_it() {
        let mut numbering = ControlCommandNumbering::with_initial_commands(2);

        let first = numbering.next_frame();
        assert_eq!(first.number, 1);
        assert_eq!(first.guard_flag, 0);
        assert_eq!(
            first.origin,
            ControlCommandOrigin::Initial {
                completes_batch: false
            }
        );

        let last = numbering.next_frame();
        assert_eq!(last.number, 2);
        assert_eq!(last.guard_flag, 0);
        assert!(last.origin.completes_initial_batch());

        let stdin = numbering.next_frame();
        assert_eq!(stdin.number, 3);
        assert_eq!(stdin.guard_flag, 1);
        assert_eq!(stdin.origin, ControlCommandOrigin::Stdin);
    }

    #[test]
    fn synthetic_initial_ack_places_the_first_real_command_on_stdin() {
        let mut numbering = ControlCommandNumbering::after_initial_ack();

        let stdin = numbering.next_frame();
        assert_eq!(stdin.number, 2);
        assert_eq!(stdin.guard_flag, 1);
        assert_eq!(stdin.origin, ControlCommandOrigin::Stdin);
    }
}