Skip to main content

muxr_core/protocol/
terminal.rs

1use std::num::NonZeroU16;
2
3use rootcause::report;
4use serde::Deserialize;
5use serde::Serialize;
6
7#[derive(rkyv::Archive, Clone, Debug, Deserialize, rkyv::Deserialize, Eq, PartialEq, Serialize, rkyv::Serialize)]
8pub struct TerminalSize {
9    cols: NonZeroU16,
10    rows: NonZeroU16,
11}
12
13impl TerminalSize {
14    /// Build terminal dimensions, rejecting zero values before they reach the PTY layer.
15    ///
16    /// # Errors
17    /// - Columns or rows are zero.
18    pub fn new(cols: u16, rows: u16) -> rootcause::Result<Self> {
19        let Some(cols) = NonZeroU16::new(cols) else {
20            return Err(report!("invalid muxr terminal size").attach("cols=0"));
21        };
22        let Some(rows) = NonZeroU16::new(rows) else {
23            return Err(report!("invalid muxr terminal size").attach("rows=0"));
24        };
25
26        Ok(Self { cols, rows })
27    }
28
29    /// Return terminal columns.
30    #[must_use]
31    pub const fn cols(&self) -> u16 {
32        self.cols.get()
33    }
34
35    /// Return terminal rows.
36    #[must_use]
37    pub const fn rows(&self) -> u16 {
38        self.rows.get()
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use rstest::rstest;
45    use test_that::prelude::*;
46
47    use super::*;
48
49    #[rstest]
50    #[case::zero_cols(r#"{"cols":0,"rows":24}"#)]
51    #[case::zero_rows(r#"{"cols":80,"rows":0}"#)]
52    fn test_terminal_size_deserialize_when_dimension_is_zero_returns_error(#[case] raw: &str) {
53        assert_that!(serde_json::from_str::<TerminalSize>(raw), err(anything()));
54    }
55
56    #[rstest]
57    #[case::zero_cols(0, 24)]
58    #[case::zero_rows(80, 0)]
59    fn test_terminal_size_new_when_dimension_is_zero_returns_error(#[case] cols: u16, #[case] rows: u16) {
60        assert_that!(TerminalSize::new(cols, rows), err(anything()));
61    }
62
63    #[test]
64    fn test_terminal_size_new_when_dimensions_are_nonzero_returns_size() -> rootcause::Result<()> {
65        let size = TerminalSize::new(120, 40)?;
66
67        assert_that!(size.cols(), eq(120));
68        assert_that!(size.rows(), eq(40));
69        Ok(())
70    }
71}