vt_push_parser/
signature.rs

1//! Escape sequence signature matching.
2
3use std::ops::Range;
4
5use crate::event::{VTEvent, VTIntermediate};
6
7const CSI: u8 = b'[';
8const SS3: u8 = b'O';
9const DCS: u8 = b'P';
10const OSC: u8 = b']';
11
12/// A signature for an escape sequence.
13pub struct VTEscapeSignature {
14    pub prefix: u8,
15    pub private: Option<u8>,
16    pub intermediates: VTIntermediate,
17    pub final_byte: u8,
18    pub param_count: Range<u8>,
19}
20
21impl VTEscapeSignature {
22    pub const fn with_private(self, private: u8) -> Self {
23        Self {
24            private: Some(private),
25            ..self
26        }
27    }
28
29    pub const fn with_intermediate(self, intermediate: u8) -> Self {
30        Self {
31            intermediates: VTIntermediate::one(intermediate),
32            ..self
33        }
34    }
35
36    pub const fn with_params_exact(self, param_count: u8) -> Self {
37        Self {
38            param_count: param_count..param_count + 1,
39            ..self
40        }
41    }
42
43    pub const fn with_params_count(self, param_count: Range<u8>) -> Self {
44        Self {
45            param_count,
46            ..self
47        }
48    }
49
50    pub const fn csi(final_byte: u8) -> Self {
51        Self {
52            prefix: CSI,
53            final_byte,
54            param_count: 0..1,
55            intermediates: VTIntermediate::empty(),
56            private: None,
57        }
58    }
59
60    pub const fn ss3(final_byte: u8) -> Self {
61        Self {
62            prefix: SS3,
63            private: None,
64            intermediates: VTIntermediate::empty(),
65            final_byte,
66            param_count: 0..1,
67        }
68    }
69
70    pub const fn dcs(final_byte: u8) -> Self {
71        Self {
72            prefix: DCS,
73            private: None,
74            intermediates: VTIntermediate::empty(),
75            final_byte,
76            param_count: 0..1,
77        }
78    }
79
80    pub const fn osc(final_byte: u8) -> Self {
81        Self {
82            prefix: OSC,
83            private: None,
84            intermediates: VTIntermediate::empty(),
85            final_byte,
86            param_count: 0..1,
87        }
88    }
89
90    pub fn matches(&self, entry: &VTEvent) -> bool {
91        // TODO: const
92        match entry {
93            VTEvent::Esc(esc) => {
94                self.final_byte == esc.final_byte && self.intermediates.const_eq(&esc.intermediates)
95            }
96            VTEvent::Csi(csi) => {
97                self.prefix == CSI
98                    && self.final_byte == csi.final_byte
99                    && self.intermediates.const_eq(&csi.intermediates)
100                    && self.const_private_eq(&csi.private)
101                    && self.const_contains(csi.params.len())
102            }
103            VTEvent::DcsStart(dcs_start) => {
104                self.prefix == DCS
105                    && self.final_byte == dcs_start.final_byte
106                    && self.intermediates.const_eq(&dcs_start.intermediates)
107                    && self.private == dcs_start.private
108                    && self.const_contains(dcs_start.params.len())
109            }
110            _ => false,
111        }
112    }
113
114    const fn const_private_eq(&self, other: &Option<u8>) -> bool {
115        match (self.private, other) {
116            (Some(a), Some(b)) => a == *b,
117            (None, None) => true,
118            _ => false,
119        }
120    }
121
122    fn const_contains(&self, len: usize) -> bool {
123        // TODO: const
124        self.param_count.contains(&(len as u8))
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::VTPushParser;
132
133    const CURSOR_POSITION_REPORT: VTEscapeSignature =
134        VTEscapeSignature::csi(b'n').with_params_exact(2);
135
136    #[test]
137    fn test_matches() {
138        let input = b"\x1b[1;2n";
139        let mut found = false;
140        VTPushParser::decode_buffer(input, |event| {
141            assert!(!found);
142            found = true;
143            assert!(CURSOR_POSITION_REPORT.matches(&event));
144        });
145        assert!(found);
146    }
147}