1use std::error::Error;
2
3pub type ParseError = Box<dyn Error + Send + Sync>;
4pub type ParsedCursor = (String, String, u64, u64);
5
6pub fn parse_match_string(s: &str) -> std::result::Result<Vec<u8>, ParseError> {
7 parse_match_bytes(s.as_bytes())
8}
9
10pub fn parse_cursor(cursor: &str) -> std::result::Result<ParsedCursor, ParseError> {
11 let mut seqnum_id = String::new();
12 let mut boot_id = String::new();
13 let mut realtime = None;
14 let mut seqnum = None;
15
16 for part in cursor.split(';') {
17 let split = part.split_once('=');
18 if split.is_none() {
19 return Err("invalid cursor: malformed segment".into());
20 }
21 let (key, value) = split.unwrap();
22 if key.is_empty() {
23 return Err("invalid cursor: empty key".into());
24 }
25 match key {
26 "s" => seqnum_id = value.to_string(),
27 "j" => boot_id = value.to_string(),
28 "c" => realtime = Some(u64::from_str_radix(value, 16)?),
29 "n" => seqnum = Some(value.parse()?),
30 _ => {}
31 }
32 }
33
34 if seqnum_id.is_empty() || boot_id.is_empty() {
35 return Err("invalid cursor: missing id".into());
36 }
37
38 Ok((
39 seqnum_id,
40 boot_id,
41 realtime.ok_or("invalid cursor: missing realtime")?,
42 seqnum.ok_or("invalid cursor: missing seqnum")?,
43 ))
44}
45
46pub fn parse_match_bytes(data: &[u8]) -> std::result::Result<Vec<u8>, ParseError> {
47 let eq = data.iter().position(|byte| *byte == b'=');
48 if eq.is_none() {
49 return Err("EINVAL: missing '=' separator".into());
50 }
51 let eq = eq.unwrap();
52 let key = &data[..eq];
53 if key.is_empty() || key[0].is_ascii_digit() {
54 return Err("EINVAL: invalid field name".into());
55 }
56 for byte in key {
57 if !byte.is_ascii_uppercase() && !byte.is_ascii_digit() && *byte != b'_' {
58 return Err("EINVAL: invalid field name".into());
59 }
60 }
61 return Ok(data.to_vec());
62}