1use std::io::Read;
4
5const LF: u8 = 10;
7
8const CR: u8 = 13;
10
11const AT: u8 = 64;
13
14#[derive(Debug)]
16pub enum AtAtParseItem {
17 KeyValue(String, String),
19 Data(Vec<u8>),
21}
22
23enum State {
25 Waiting,
27
28 LineStart,
30
31 GatherName(Vec<u8>),
33
34 GatherValue(Vec<u8>, Vec<u8>),
36
37 FirstAt(Vec<u8>, Vec<u8>),
39}
40
41pub 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 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 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}