Skip to main content

journal/
parse.rs

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}