scf/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3use core::{cell::Cell, str};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub enum Token<'a> {
7	Begin,
8	End,
9	Str(&'a str),
10}
11
12impl<'a> Token<'a> {
13	pub fn into_str(self) -> Option<&'a str> {
14		match self {
15			Self::Str(s) => Some(s),
16			_ => None,
17		}
18	}
19}
20
21pub struct Iter<'a> {
22	data: &'a [u8],
23	index: usize,
24}
25
26impl<'a> Iterator for Iter<'a> {
27	type Item = Result<Token<'a>, Error>;
28
29	fn next(&mut self) -> Option<Self::Item> {
30		let ret_str = |s| {
31			str::from_utf8(s)
32				.map_err(|_| Error::InvalidUtf8)
33				.map(|s| Token::Str(s))
34		};
35		loop {
36			let c = self.data.get(self.index)?;
37			self.index += 1;
38			match c {
39				c if c.is_ascii_whitespace() => {}
40				b'(' => return Some(Ok(Token::Begin)),
41				b')' => return Some(Ok(Token::End)),
42				lim @ b'"' | lim @ b'\'' => loop {
43					let start = self.index;
44					while let Some(&c) = self.data.get(self.index) {
45						self.index += 1;
46						match c {
47							b'\\' => self.index += 1,
48							c if c == *lim => {
49								return Some(ret_str(&self.data[start..self.index - 1]));
50							}
51							_ => {}
52						}
53					}
54					return Some(Err(Error::UnterminatedQuote));
55				},
56				b';' => {
57					while self.data.get(self.index).map_or(false, |c| *c != b'\n') {
58						self.index += 1;
59					}
60				}
61				_ => loop {
62					let start = self.index - 1;
63					while let Some(&c) = self.data.get(self.index) {
64						self.index += 1;
65						match c {
66							c if c == b'(' || c == b')' || c.is_ascii_whitespace() => {
67								self.index -= 1;
68								break;
69							}
70							_ => {}
71						}
72					}
73					return Some(ret_str(&self.data[start..self.index]));
74				},
75			}
76		}
77	}
78}
79
80#[derive(Debug)]
81#[must_use = "an error may have occured"]
82pub struct Groups<'a> {
83	data: &'a [u8],
84	index: Cell<usize>,
85}
86
87impl<'a> Groups<'a> {
88	pub fn iter(&mut self) -> GroupsIter<'a, '_> {
89		GroupsIter { inner: Some(self) }
90	}
91
92	pub fn into_error(self) -> Option<Error> {
93		Error::from_num(self.index.get())
94	}
95}
96
97#[derive(Debug)]
98pub struct GroupsIter<'a, 'b> {
99	inner: Option<&'b Groups<'a>>,
100}
101
102impl<'a, 'b> GroupsIter<'a, 'b> {
103	pub fn next_str(&mut self) -> Option<&'a str> {
104		self.next().and_then(|e| e.into_str())
105	}
106
107	pub fn next_group(&mut self) -> Option<GroupsIter<'a, 'b>> {
108		self.next().and_then(|e| e.into_group())
109	}
110}
111
112impl<'a, 'b> Iterator for GroupsIter<'a, 'b> {
113	type Item = Item<'a, 'b>;
114
115	fn next(&mut self) -> Option<Self::Item> {
116		let r = self.inner?;
117		let mut it = Iter {
118			data: r.data,
119			index: r.index.get(),
120		};
121		if (it.index as isize) < 0 {
122			return None;
123		}
124		let tk = it.next();
125		r.index.set(it.index);
126		match tk {
127			None => None,
128			Some(Err(e)) => {
129				r.index.set(e.into_num());
130				None
131			}
132			Some(Ok(tk)) => Some(match tk {
133				Token::Str(s) => Item::Str(s),
134				Token::Begin => Item::Group(Self { inner: self.inner }),
135				Token::End => {
136					self.inner = None;
137					return None
138				}
139			}),
140		}
141	}
142}
143
144impl Drop for GroupsIter<'_, '_> {
145	fn drop(&mut self) {
146		for _ in self {}
147	}
148}
149
150impl core::iter::FusedIterator for GroupsIter<'_, '_> {}
151
152#[derive(Debug)]
153pub enum Item<'a, 'b> {
154	Str(&'a str),
155	Group(GroupsIter<'a, 'b>),
156}
157
158impl<'a, 'b> Item<'a, 'b> {
159	pub fn into_str(self) -> Option<&'a str> {
160		match self {
161			Self::Str(s) => Some(s),
162			_ => None,
163		}
164	}
165
166	pub fn into_group(self) -> Option<GroupsIter<'a, 'b>> {
167		match self {
168			Self::Group(g) => Some(g),
169			_ => None,
170		}
171	}
172}
173
174#[derive(Debug, PartialEq, Eq)]
175pub enum Error {
176	UnterminatedQuote,
177	InvalidSymbolChar,
178	InvalidUtf8,
179}
180
181impl Error {
182	fn into_num(self) -> usize {
183		(match self {
184			Self::UnterminatedQuote => -1,
185			Self::InvalidSymbolChar => -2,
186			Self::InvalidUtf8 => -3,
187		}) as usize
188	}
189
190	fn from_num(n: usize) -> Option<Self> {
191		Some(match n as isize {
192			-1 => Self::UnterminatedQuote,
193			-2 => Self::InvalidSymbolChar,
194			-3 => Self::InvalidUtf8,
195			_ => return None,
196		})
197	}
198}
199
200#[deprecated(note = "use `parse2`, which is less error-prone")]
201pub fn parse<'a>(data: &'a [u8]) -> Iter<'a> {
202	Iter { data, index: 0 }
203}
204
205pub fn parse2<'a>(data: &'a [u8]) -> Groups<'a> {
206	Groups { data, index: 0.into() }
207}
208
209#[cfg(test)]
210mod test {
211	use super::*;
212
213	#[test]
214	fn example_pci() {
215		let t = br#"(pci-drivers
216	(1af4 ; Red Hat
217		(1000 "drivers/pci/virtio/net")
218		(1001 "drivers/pci/virtio/blk")
219		(1050 "drivers/pci/virtio/gpu"))
220	(8086 ; Intel
221		(1616 "drivers/pci/intel/hd graphics"))) ; intentional space"#;
222		#[allow(deprecated)]
223		let mut it = parse(t);
224		assert_eq!(it.next(), Some(Ok(Token::Begin)));
225		assert_eq!(it.next(), Some(Ok(Token::Str("pci-drivers"))));
226		assert_eq!(it.next(), Some(Ok(Token::Begin)));
227		assert_eq!(it.next(), Some(Ok(Token::Str("1af4"))));
228		assert_eq!(it.next(), Some(Ok(Token::Begin)));
229		assert_eq!(it.next(), Some(Ok(Token::Str("1000"))));
230		assert_eq!(it.next(), Some(Ok(Token::Str("drivers/pci/virtio/net"))));
231		assert_eq!(it.next(), Some(Ok(Token::End)));
232		assert_eq!(it.next(), Some(Ok(Token::Begin)));
233		assert_eq!(it.next(), Some(Ok(Token::Str("1001"))));
234		assert_eq!(it.next(), Some(Ok(Token::Str("drivers/pci/virtio/blk"))));
235		assert_eq!(it.next(), Some(Ok(Token::End)));
236		assert_eq!(it.next(), Some(Ok(Token::Begin)));
237		assert_eq!(it.next(), Some(Ok(Token::Str("1050"))));
238		assert_eq!(it.next(), Some(Ok(Token::Str("drivers/pci/virtio/gpu"))));
239		assert_eq!(it.next(), Some(Ok(Token::End)));
240		assert_eq!(it.next(), Some(Ok(Token::End)));
241		assert_eq!(it.next(), Some(Ok(Token::Begin)));
242		assert_eq!(it.next(), Some(Ok(Token::Str("8086"))));
243		assert_eq!(it.next(), Some(Ok(Token::Begin)));
244		assert_eq!(it.next(), Some(Ok(Token::Str("1616"))));
245		assert_eq!(
246			it.next(),
247			Some(Ok(Token::Str("drivers/pci/intel/hd graphics")))
248		);
249		assert_eq!(it.next(), Some(Ok(Token::End)));
250		assert_eq!(it.next(), Some(Ok(Token::End)));
251		assert_eq!(it.next(), Some(Ok(Token::End)));
252		assert_eq!(it.next(), None);
253	}
254
255	#[test]
256	fn example_pci_groups() {
257		let t = br#"(pci-drivers
258	(1af4 ; Red Hat
259		(1000 "drivers/pci/virtio/net")
260		(1001 "drivers/pci/virtio/blk")
261		(1050 "drivers/pci/virtio/gpu"))
262	(8086 ; Intel
263		(1616 "drivers/pci/intel/hd graphics"))) ; intentional space"#;
264		#[track_caller]
265		fn string<'a, 'b>(it: &mut GroupsIter<'a, 'b>) -> &'a str {
266			it.next().unwrap().into_str().unwrap()
267		}
268		#[track_caller]
269		fn group<'a, 'b>(it: &mut GroupsIter<'a, 'b>) -> GroupsIter<'a, 'b> {
270			it.next().unwrap().into_group().unwrap()
271		}
272		#[track_caller]
273		fn none<'a, 'b>(it: &mut GroupsIter<'a, 'b>) {
274			assert!(it.next().is_none());
275			// Multiple times as a sanity check
276			assert!(it.next().is_none());
277			assert!(it.next().is_none());
278		}
279
280		let mut cf = parse2(t);
281		{
282			let mut it = cf.iter();
283			let mut it2 = group(&mut it);
284			assert_eq!(string(&mut it2), "pci-drivers");
285			let mut it3 = group(&mut it2);
286			assert_eq!(string(&mut it3), "1af4");
287			let mut it4 = group(&mut it3);
288			assert_eq!(string(&mut it4), "1000");
289			assert_eq!(string(&mut it4), "drivers/pci/virtio/net");
290			none(&mut it4);
291			let mut it4 = group(&mut it3);
292			assert_eq!(string(&mut it4), "1001");
293			assert_eq!(string(&mut it4), "drivers/pci/virtio/blk");
294			none(&mut it4);
295			let mut it4 = group(&mut it3);
296			assert_eq!(string(&mut it4), "1050");
297			assert_eq!(string(&mut it4), "drivers/pci/virtio/gpu");
298			none(&mut it4);
299			none(&mut it3);
300			let mut it3 = group(&mut it2);
301			assert_eq!(string(&mut it3), "8086");
302			let mut it4 = group(&mut it3);
303			assert_eq!(string(&mut it4), "1616");
304			assert_eq!(string(&mut it4), "drivers/pci/intel/hd graphics");
305			none(&mut it4);
306			none(&mut it3);
307			none(&mut it2);
308			none(&mut it);
309		}
310		assert!(cf.into_error().is_none());
311	}
312
313	#[test]
314	fn partial_iter_group() {
315		let t = br#"(pci-drivers
316	(1af4 ; Red Hat
317		(1000 "drivers/pci/virtio/net")
318		(1001 "drivers/pci/virtio/blk")
319		(1050 "drivers/pci/virtio/gpu"))
320	(8086 ; Intel
321		(1616 "drivers/pci/intel/hd graphics"))) ; intentional space"#;
322		#[track_caller]
323		fn string<'a, 'b>(it: &mut GroupsIter<'a, 'b>) -> &'a str {
324			it.next().unwrap().into_str().unwrap()
325		}
326		#[track_caller]
327		fn group<'a, 'b>(it: &mut GroupsIter<'a, 'b>) -> GroupsIter<'a, 'b> {
328			it.next().unwrap().into_group().unwrap()
329		}
330		#[track_caller]
331		fn none<'a, 'b>(it: &mut GroupsIter<'a, 'b>) {
332			assert!(it.next().is_none());
333			// Multiple times as a sanity check
334			assert!(it.next().is_none());
335			assert!(it.next().is_none());
336		}
337
338		let mut cf = parse2(t);
339		{
340			let mut it = cf.iter();
341			let mut it2 = group(&mut it);
342			assert_eq!(string(&mut it2), "pci-drivers");
343			let mut it3 = group(&mut it2);
344			assert_eq!(string(&mut it3), "1af4");
345			let mut it4 = group(&mut it3);
346			assert_eq!(string(&mut it4), "1000");
347			assert_eq!(string(&mut it4), "drivers/pci/virtio/net");
348			none(&mut it4);
349			let mut it4 = group(&mut it3);
350			assert_eq!(string(&mut it4), "1001");
351			drop(it4);
352			drop(it3);
353			let mut it3 = group(&mut it2);
354			assert_eq!(string(&mut it3), "8086");
355			let mut it4 = group(&mut it3);
356			assert_eq!(string(&mut it4), "1616");
357			assert_eq!(string(&mut it4), "drivers/pci/intel/hd graphics");
358			none(&mut it4);
359			none(&mut it3);
360			none(&mut it2);
361			none(&mut it);
362		}
363		assert!(cf.into_error().is_none());
364	}
365}