vt_push_parser/
signature.rs

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