cargo_task/
at_at.rs

1//! AtAt Encoded KV Parsing
2
3use std::io::Read;
4
5/// line feed
6const LF: u8 = 10;
7
8/// carriage return
9const CR: u8 = 13;
10
11/// '@' character
12const AT: u8 = 64;
13
14/// Type returned from AtAt parsing.
15#[derive(Debug)]
16pub enum AtAtParseItem {
17    /// We parsed out a key-value item.
18    KeyValue(String, String),
19    /// Raw data passing through parser.
20    Data(Vec<u8>),
21}
22
23/// internal parse state
24enum State {
25    /// middle of not @@ characters
26    Waiting,
27
28    /// at line start - look for @
29    LineStart,
30
31    /// we found an '@' at line-start - gather a name
32    GatherName(Vec<u8>),
33
34    /// we found a second '@' start gathering value
35    GatherValue(Vec<u8>, Vec<u8>),
36
37    /// we found a first termination '@' if we get another it'll be a N/V
38    FirstAt(Vec<u8>, Vec<u8>),
39}
40
41/// AtAt Encoded KV Parser
42pub struct AtAtParser<R: Read> {
43    reader: R,
44    raw_buf: [u8; 4096],
45    state: Option<State>,
46    eof: bool,
47}
48
49impl<R: Read> AtAtParser<R> {
50    /// Wrap a reader in an AtAt parser.
51    pub fn new(reader: R) -> Self {
52        Self {
53            reader,
54            raw_buf: [0; 4096],
55            state: Some(State::LineStart),
56            eof: false,
57        }
58    }
59
60    /// Execute one iteration of parsing.
61    /// A 'None' result indicates the reader is complete (EOF).
62    /// An empty Vec result may simply mean we need to wait for more data.
63    pub fn parse(&mut self) -> Option<Vec<AtAtParseItem>> {
64        if self.eof {
65            return None;
66        }
67
68        let read = match self.reader.read(&mut self.raw_buf) {
69            Ok(read) => read,
70            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
71                return Some(Vec::with_capacity(0))
72            }
73            Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {
74                return Some(Vec::with_capacity(0))
75            }
76            Err(e) => crate::ct_fatal!("{:?}", e),
77        };
78
79        if read == 0 {
80            self.eof = true;
81            self.state = None;
82            return None;
83        }
84
85        let mut out = vec![AtAtParseItem::Data(self.raw_buf[..read].to_vec())];
86
87        for c in self.raw_buf[..read].iter().cloned() {
88            self.state = Some(match self.state.take().unwrap() {
89                State::Waiting => {
90                    if c == LF || c == CR {
91                        State::LineStart
92                    } else {
93                        State::Waiting
94                    }
95                }
96                State::LineStart => {
97                    if c == AT {
98                        State::GatherName(Vec::new())
99                    } else if c == LF || c == CR {
100                        State::LineStart
101                    } else {
102                        State::Waiting
103                    }
104                }
105                State::GatherName(mut name) => {
106                    if c == AT {
107                        State::GatherValue(name, Vec::new())
108                    } else {
109                        name.push(c);
110                        State::GatherName(name)
111                    }
112                }
113                State::GatherValue(name, mut value) => {
114                    if c == AT {
115                        State::FirstAt(name, value)
116                    } else {
117                        value.push(c);
118                        State::GatherValue(name, value)
119                    }
120                }
121                State::FirstAt(name, mut value) => {
122                    if c == AT {
123                        out.push(AtAtParseItem::KeyValue(
124                            String::from_utf8_lossy(&name).trim().to_string(),
125                            String::from_utf8_lossy(&value).trim().to_string(),
126                        ));
127                        State::Waiting
128                    } else {
129                        value.push(64);
130                        State::GatherValue(name, value)
131                    }
132                }
133            });
134        }
135
136        Some(out)
137    }
138}