1use std::fmt;
29
30#[derive(Copy, Clone)]
32pub struct Params<'a> {
33 raw: &'a [u8],
34}
35
36impl<'a> Params<'a> {
37 pub const EMPTY: Self = Self { raw: &[] };
39
40 #[inline]
43 pub fn from_raw(raw: &'a [u8]) -> Self {
44 Self { raw }
45 }
46
47 #[inline]
49 pub fn raw(&self) -> &'a [u8] {
50 self.raw
51 }
52
53 #[inline]
55 pub fn is_empty(&self) -> bool {
56 self.raw.is_empty()
57 }
58
59 pub fn len(&self) -> usize {
62 if self.raw.is_empty() {
63 0
64 } else {
65 self.raw.iter().filter(|&&b| b == b';').count() + 1
66 }
67 }
68
69 pub fn iter(&self) -> ParamIter<'a> {
71 ParamIter {
72 inner: if self.raw.is_empty() {
73 None
74 } else {
75 Some(self.raw.split(is_semicolon))
76 },
77 }
78 }
79
80 pub fn group(&self, idx: usize) -> Option<Group<'a>> {
82 self.iter().nth(idx)
83 }
84
85 pub fn get(&self, idx: usize) -> Option<u32> {
88 self.group(idx).and_then(|g| g.first())
89 }
90
91 pub fn get_or(&self, idx: usize, default: u32) -> u32 {
94 self.get(idx).unwrap_or(default)
95 }
96
97 pub fn slice_from(&self, start: usize) -> Params<'a> {
101 let mut cur = self.raw;
102 let mut skipped = 0;
103 while skipped < start {
104 match cur.iter().position(|&b| b == b';') {
105 Some(p) => {
106 cur = &cur[p + 1..];
107 skipped += 1;
108 }
109 None => return Params::EMPTY,
110 }
111 }
112 Params { raw: cur }
113 }
114}
115
116impl<'a> fmt::Debug for Params<'a> {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.debug_list()
119 .entries(self.iter().map(|g| g.first()))
120 .finish()
121 }
122}
123
124impl<'a> IntoIterator for Params<'a> {
125 type Item = Group<'a>;
126 type IntoIter = ParamIter<'a>;
127 fn into_iter(self) -> Self::IntoIter {
128 self.iter()
129 }
130}
131
132#[derive(Copy, Clone)]
136pub struct Group<'a> {
137 raw: &'a [u8],
138}
139
140impl<'a> Group<'a> {
141 pub const EMPTY: Self = Self { raw: &[] };
143
144 #[inline]
146 pub fn from_raw(raw: &'a [u8]) -> Self {
147 Self { raw }
148 }
149
150 #[inline]
152 pub fn raw(&self) -> &'a [u8] {
153 self.raw
154 }
155
156 #[inline]
159 pub fn is_empty(&self) -> bool {
160 self.raw.is_empty()
161 }
162
163 pub fn len(&self) -> usize {
166 if self.raw.is_empty() {
167 1
168 } else {
169 self.raw.iter().filter(|&&b| b == b':').count() + 1
170 }
171 }
172
173 pub fn iter(&self) -> SubIter<'a> {
175 SubIter {
176 inner: self.raw.split(is_colon),
177 }
178 }
179
180 pub fn first(&self) -> Option<u32> {
182 self.iter().next().flatten()
183 }
184
185 pub fn nth(&self, i: usize) -> Option<u32> {
188 self.iter().nth(i).flatten()
189 }
190}
191
192impl<'a> fmt::Debug for Group<'a> {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 f.debug_list().entries(self.iter()).finish()
195 }
196}
197
198impl<'a> IntoIterator for Group<'a> {
199 type Item = Option<u32>;
200 type IntoIter = SubIter<'a>;
201 fn into_iter(self) -> Self::IntoIter {
202 self.iter()
203 }
204}
205
206pub struct ParamIter<'a> {
208 #[allow(clippy::type_complexity)]
211 inner: Option<std::slice::Split<'a, u8, fn(&u8) -> bool>>,
212}
213
214impl<'a> Iterator for ParamIter<'a> {
215 type Item = Group<'a>;
216 fn next(&mut self) -> Option<Group<'a>> {
217 self.inner.as_mut()?.next().map(|raw| Group { raw })
218 }
219}
220
221pub struct SubIter<'a> {
223 inner: std::slice::Split<'a, u8, fn(&u8) -> bool>,
224}
225
226impl<'a> Iterator for SubIter<'a> {
227 type Item = Option<u32>;
228 fn next(&mut self) -> Option<Option<u32>> {
229 self.inner.next().map(parse_u32)
230 }
231}
232
233fn is_semicolon(b: &u8) -> bool {
234 *b == b';'
235}
236
237fn is_colon(b: &u8) -> bool {
238 *b == b':'
239}
240
241#[derive(Copy, Clone, Debug)]
259pub struct ControlSequence<'a> {
260 private: Option<u8>,
261 params: Params<'a>,
262 intermediate: Option<u8>,
263 final_byte: u8,
264}
265
266impl<'a> ControlSequence<'a> {
267 pub fn parse(body: &'a [u8]) -> Option<(Self, &'a [u8])> {
281 let (private, rest) = match body.first() {
282 Some(&b) if (0x3c..=0x3f).contains(&b) => (Some(b), &body[1..]),
283 _ => (None, body),
284 };
285
286 let mid = rest
291 .iter()
292 .position(|&b| !(0x30..=0x3b).contains(&b))
293 .unwrap_or(rest.len());
294 let params = Params::from_raw(&rest[..mid]);
295
296 let (intermediate, final_pos) = match rest.get(mid) {
300 Some(&b) if (0x20..=0x2f).contains(&b) => (Some(b), mid + 1),
301 _ => (None, mid),
302 };
303
304 let final_byte = *rest.get(final_pos)?;
305 if !(0x40..=0x7e).contains(&final_byte) {
306 return None;
307 }
308
309 Some((
310 Self {
311 private,
312 params,
313 intermediate,
314 final_byte,
315 },
316 &rest[final_pos + 1..],
317 ))
318 }
319
320 pub fn private(&self) -> Option<u8> {
323 self.private
324 }
325
326 pub fn params(&self) -> Params<'a> {
328 self.params
329 }
330
331 pub fn intermediate(&self) -> Option<u8> {
333 self.intermediate
334 }
335
336 pub fn final_byte(&self) -> u8 {
339 self.final_byte
340 }
341}
342
343fn parse_u32(bytes: &[u8]) -> Option<u32> {
344 if bytes.is_empty() {
345 return None;
346 }
347 let mut acc: u32 = 0;
348 for &b in bytes {
349 if !b.is_ascii_digit() {
350 return None;
351 }
352 acc = acc.checked_mul(10)?.checked_add((b - b'0') as u32)?;
353 }
354 Some(acc)
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 fn flat(p: Params<'_>) -> Vec<Option<u32>> {
362 p.iter().map(|g| g.first()).collect()
363 }
364
365 fn nested(p: Params<'_>) -> Vec<Vec<Option<u32>>> {
366 p.iter().map(|g| g.iter().collect()).collect()
367 }
368
369 #[test]
370 fn flat_distinguishes_absent_from_explicit_zero() {
371 assert_eq!(flat(Params::from_raw(b"")), Vec::<Option<u32>>::new());
372 assert_eq!(flat(Params::from_raw(b";")), vec![None, None]);
373 assert_eq!(
374 flat(Params::from_raw(b"0;;3")),
375 vec![Some(0), None, Some(3)]
376 );
377 assert_eq!(flat(Params::from_raw(b";5")), vec![None, Some(5)]);
378 assert_eq!(
379 flat(Params::from_raw(b"27;5;128512")),
380 vec![Some(27), Some(5), Some(128512)]
381 );
382 }
383
384 #[test]
385 fn flat_takes_first_colon_subparam() {
386 assert_eq!(flat(Params::from_raw(b"4:3;1")), vec![Some(4), Some(1)]);
387 }
388
389 #[test]
390 fn nested_preserves_colon_groups() {
391 assert_eq!(
392 nested(Params::from_raw(b"")),
393 Vec::<Vec<Option<u32>>>::new()
394 );
395 assert_eq!(
396 nested(Params::from_raw(b"38:2::255:100:50")),
397 vec![vec![
398 Some(38),
399 Some(2),
400 None,
401 Some(255),
402 Some(100),
403 Some(50),
404 ]]
405 );
406 assert_eq!(
407 nested(Params::from_raw(b"1;31;48:5:200")),
408 vec![
409 vec![Some(1)],
410 vec![Some(31)],
411 vec![Some(48), Some(5), Some(200)],
412 ]
413 );
414 }
415
416 #[test]
417 fn nested_empty_slots() {
418 assert_eq!(
419 nested(Params::from_raw(b";1;")),
420 vec![vec![None], vec![Some(1)], vec![None]]
421 );
422 assert_eq!(nested(Params::from_raw(b"4:")), vec![vec![Some(4), None]]);
423 }
424
425 #[test]
426 fn get_or_returns_default_only_for_absent() {
427 let p = Params::from_raw(b"5;;0;7");
429 assert_eq!(p.get_or(0, 1), 5);
430 assert_eq!(p.get_or(1, 1), 1);
431 assert_eq!(p.get_or(2, 1), 0);
432 assert_eq!(p.get_or(3, 1), 7);
433 assert_eq!(p.get_or(4, 1), 1);
434 assert_eq!(Params::EMPTY.get_or(0, 42), 42);
435 }
436
437 #[test]
438 fn len_counts_groups() {
439 assert_eq!(Params::from_raw(b"").len(), 0);
440 assert_eq!(Params::from_raw(b"1").len(), 1);
441 assert_eq!(Params::from_raw(b";").len(), 2);
442 assert_eq!(Params::from_raw(b"1;2;3").len(), 3);
443 assert_eq!(Params::from_raw(b"1;").len(), 2);
444 }
445
446 #[test]
447 fn slice_from_skips_groups() {
448 let p = Params::from_raw(b"1;2;3;4");
449 assert_eq!(
450 flat(p.slice_from(0)),
451 vec![Some(1), Some(2), Some(3), Some(4)]
452 );
453 assert_eq!(flat(p.slice_from(2)), vec![Some(3), Some(4)]);
454 assert_eq!(flat(p.slice_from(4)), Vec::<Option<u32>>::new());
455 assert_eq!(flat(p.slice_from(99)), Vec::<Option<u32>>::new());
456 }
457
458 #[test]
459 fn debug_format_matches_flat_list() {
460 let p = Params::from_raw(b"1;2");
461 assert_eq!(format!("{:?}", p), "[Some(1), Some(2)]");
462 }
463
464 #[test]
465 fn control_sequence_splits_each_region() {
466 let (s, rest) = ControlSequence::parse(b"?2048h").unwrap();
468 assert_eq!(s.private(), Some(b'?'));
469 assert_eq!(s.params().get_or(0, 0), 2048);
470 assert_eq!(s.intermediate(), None);
471 assert_eq!(s.final_byte(), b'h');
472 assert!(rest.is_empty());
473
474 let (s, _) = ControlSequence::parse(b"2 q").unwrap();
476 assert_eq!(s.params().get_or(0, 0), 2);
477 assert_eq!(s.intermediate(), Some(b' '));
478 assert_eq!(s.final_byte(), b'q');
479
480 let (s, _) = ControlSequence::parse(b" q").unwrap();
481 assert_eq!(s.params().get_or(0, 0), 0, "an omitted parameter is 0");
482 assert_eq!(s.intermediate(), Some(b' '));
483
484 let (s, _) = ControlSequence::parse(b"38:2::255:100:50m").unwrap();
486 assert_eq!(s.final_byte(), b'm');
487 assert_eq!(s.params().group(0).unwrap().nth(3), Some(255));
488 }
489
490 #[test]
491 fn control_sequence_returns_what_follows_the_final_byte() {
492 let (s, rest) = ControlSequence::parse(b"1$r2 q").unwrap();
495 assert_eq!(s.params().get_or(0, 0), 1);
496 assert_eq!(s.intermediate(), Some(b'$'));
497 assert_eq!(s.final_byte(), b'r');
498 assert_eq!(rest, b"2 q");
499 }
500
501 #[test]
502 fn control_sequence_rejects_what_it_cannot_name() {
503 assert!(ControlSequence::parse(b"").is_none());
505 assert!(ControlSequence::parse(b"12").is_none());
506 assert!(ControlSequence::parse(b"2 ").is_none());
507
508 assert!(ControlSequence::parse(b"1!!p").is_none());
512 }
513}