vt_push_parser/
signature.rs1use 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
12pub 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 match entry {
93 VTEvent::Esc(esc) => {
94 self.prefix == 27
95 && self.final_byte == esc.final_byte
96 && self.intermediates.const_eq(&esc.intermediates)
97 }
98 VTEvent::Csi(csi) => {
99 self.prefix == CSI
100 && self.final_byte == csi.final_byte
101 && self.intermediates.const_eq(&csi.intermediates)
102 && self.const_private_eq(&csi.private)
103 && self.const_contains(csi.params.len())
104 }
105 VTEvent::DcsStart(dcs_start) => {
106 self.prefix == DCS
107 && self.final_byte == dcs_start.final_byte
108 && self.intermediates.const_eq(&dcs_start.intermediates)
109 && self.private == dcs_start.private
110 && self.const_contains(dcs_start.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 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}