1use std::str::Lines;
2
3#[derive(Debug, PartialEq, Eq)]
4pub enum Element<'a> {
5 SectionDefinition(&'a str),
6 Line { name: &'a str, value: &'a str },
7 Comment(&'a str),
8}
9
10impl<'a> Element<'a> {
11 pub fn is_line(&self) -> bool {
12 match self {
13 Element::Line {..} => true,
14 _ => false
15 }
16 }
17
18 pub fn is_section(&self) -> bool {
19 match self {
20 Element::SectionDefinition(_) => true,
21 _ => false
22 }
23 }
24
25 pub fn is_comment(&self) -> bool {
26 match self {
27 Element::Comment(_) => true,
28 _ => false
29 }
30 }
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub struct InvalidElement<'a>(pub &'a str);
35
36pub struct Elements<'a, T: Iterator<Item = &'a str>> {
37 iter: T,
38}
39
40pub fn parse_str(s: &str) -> Elements<Lines> {
41 Elements::new(s.lines())
42}
43
44impl<'a, T: Iterator<Item = &'a str>> Elements<'a, T> {
45 pub fn new(iter: T) -> Self {
46 Self { iter }
47 }
48}
49
50impl<'a, T: Iterator<Item = &'a str>> Iterator for Elements<'a, T> {
51 type Item = Result<Element<'a>, InvalidElement<'a>>;
52 fn next(&mut self) -> Option<Self::Item> {
53 let mut v = self.iter.next();
54 while v == Some("") {
55 v = self.iter.next();
56 }
57 match v {
58 Some(line) if line.starts_with(';') => Some(Ok(Element::Comment(
59 line.strip_prefix(";").unwrap()
60 ))),
61 Some(line) if line.starts_with('[') && line.ends_with(']') => {
62 Some(Ok(Element::SectionDefinition(
63 line.strip_prefix('[')
64 .and_then(|x| x.strip_suffix(']'))
65 .unwrap(),
66 )))
67 }
68 Some(line)
69 if line.contains(": ") =>
70 {
71 let mut parts = line.splitn(2, ": ");
72 Some(Ok(Element::Line {
73 name: parts.next().unwrap(),
74 value: parts.next().unwrap(),
75 }))
76 }
77 Some(line) => Some(Err(InvalidElement(line))),
78 None => None,
79 }
80 }
81}