Skip to main content

osc7501/
capabilities.rs

1use std::io::{self, Write};
2
3use crate::{Error, Kind, State, parser};
4
5/// A terminal host's capability reply, with borrowed state and kind lists.
6///
7/// Advertise only capabilities the host implements. Omitted lists use the
8/// fixed revision 0.4 defaults; explicit empty lists advertise no support.
9/// A default reply has the same bytes as a query. On terminal-to-program input,
10/// interpret [`parser::Event::Query`] using the caller's pending-query state.
11#[derive(Clone, Copy, Debug, Default)]
12pub struct CapabilityReply<'a> {
13    states: Option<&'a [State]>,
14    kinds: Option<&'a [Kind]>,
15}
16
17impl<'a> CapabilityReply<'a> {
18    /// Uses the fixed revision 0.4 defaults for both omitted lists.
19    #[must_use]
20    pub const fn new() -> Self {
21        Self {
22            states: None,
23            kinds: None,
24        }
25    }
26
27    /// Advertises these states in order; an empty slice advertises none.
28    #[must_use]
29    pub const fn states(mut self, states: &'a [State]) -> Self {
30        self.states = Some(states);
31        self
32    }
33
34    /// Advertises these blocking reasons in order; an empty slice advertises none.
35    #[must_use]
36    pub const fn kinds(mut self, kinds: &'a [Kind]) -> Self {
37        self.kinds = Some(kinds);
38        self
39    }
40
41    /// Encodes the complete reply before writing to the caller's writer.
42    /// Uses no heap allocation and does not flush.
43    ///
44    /// # Errors
45    /// Returns [`parser::Error::Limit`] wrapped in [`Error::Parser`] before any
46    /// output if the reply exceeds the complete-frame cap. Writer failures
47    /// return [`Error::Io`] and can leave a partial frame.
48    pub fn write_to<W: Write + ?Sized>(self, output: &mut W) -> Result<(), Error> {
49        let mut frame = [0; parser::MAX_SEQUENCE];
50        let length = self
51            .encode(&mut frame)
52            .map_err(|_| parser::Error::Limit(parser::Limit::Sequence))?;
53        output.write_all(&frame[..length])?;
54        Ok(())
55    }
56
57    fn encode(self, frame: &mut [u8]) -> io::Result<usize> {
58        let capacity = frame.len();
59        let mut remaining = frame;
60        remaining.write_all(b"\x1b]7501;?")?;
61        if let Some(states) = self.states {
62            write_list(
63                &mut remaining,
64                b":states=",
65                states.iter().copied().map(State::as_str),
66            )?;
67        }
68        if let Some(kinds) = self.kinds {
69            write_list(
70                &mut remaining,
71                b":kinds=",
72                kinds.iter().copied().map(Kind::as_str),
73            )?;
74        }
75        remaining.write_all(b"\x1b\\")?;
76        Ok(capacity - remaining.len())
77    }
78}
79
80fn write_list(
81    output: &mut impl Write,
82    key: &[u8],
83    values: impl Iterator<Item = &'static str>,
84) -> io::Result<()> {
85    output.write_all(key)?;
86    for (index, value) in values.enumerate() {
87        if index != 0 {
88            output.write_all(b",")?;
89        }
90        output.write_all(value.as_bytes())?;
91    }
92    Ok(())
93}