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 {
85                intermediates,
86                final_byte,
87            } => self.final_byte == *final_byte && self.intermediates.const_eq(intermediates),
88            VTEvent::Csi {
89                private,
90                params,
91                intermediates,
92                final_byte,
93            } => {
94                self.prefix == CSI
95                    && self.final_byte == *final_byte
96                    && self.intermediates.const_eq(intermediates)
97                    && self.const_private_eq(private)
98                    && self.const_contains(params.len())
99            }
100            VTEvent::DcsStart {
101                private,
102                params,
103                intermediates,
104                final_byte,
105            } => {
106                self.prefix == DCS
107                    && self.final_byte == *final_byte
108                    && self.intermediates.const_eq(intermediates)
109                    && self.private == *private
110                    && self.const_contains(params.len())
111            }
112            _ => false,
113        }
114    }
115
116    const fn const_private_eq(&self, other: &Option<u8>) -> bool {
117        match (self.private, other) {
118            (Some(a), Some(b)) => a == *b,
119            (None, None) => true,
120            _ => false,
121        }
122    }
123
124    fn const_contains(&self, len: usize) -> bool {
125        // TODO: const
126        self.param_count.contains(&(len as u8))
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::VTPushParser;
134
135    const CURSOR_POSITION_REPORT: VTEscapeSignature =
136        VTEscapeSignature::csi(b'n').with_params_exact(2);
137
138    #[test]
139    fn test_matches() {
140        let input = b"\x1b[1;2n";
141        let mut found = false;
142        VTPushParser::decode_buffer(input, |event| {
143            assert!(!found);
144            found = true;
145            assert!(CURSOR_POSITION_REPORT.matches(&event));
146        });
147        assert!(found);
148    }
149}