1use std::{collections::HashMap, iter::Peekable, str::Chars};
2
3use crate::error::{ParseError, ParseErrorKind};
4
5#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum ConfigNodeValue {
8 Text(String),
10 Node(ConfigNode),
12}
13
14#[derive(Clone, Debug, Eq, PartialEq, Default)]
16pub struct ConfigNode {
17 pub children: HashMap<String, ConfigNodeValue>,
19}
20
21pub struct ConfigNodeParser<'a> {
23 it: Peekable<Chars<'a>>,
24}
25
26impl ConfigNodeParser<'_> {
27 pub fn parse(text: &str) -> Result<ConfigNode, ParseError> {
29 let mut parser = ConfigNodeParser {
30 it: text.chars().peekable(),
31 };
32
33 parser.parse_confignode()
34 }
35
36 fn parse_confignode(&mut self) -> Result<ConfigNode, ParseError> {
38 let mut children = HashMap::new();
39
40 self.skip_whitespace();
41
42 loop {
43 let identifier = match self.it.peek() {
44 Some('}') | None => {
45 break;
46 }
47 Some('/') => {
48 self.it.next();
49
50 if self.it.peek() == Some(&'/') {
51 self.skip_line();
52 self.skip_whitespace();
53 continue;
54 } else {
55 let mut identifier = self.parse_identifier()?;
56 identifier.insert(0, '/');
57 identifier
58 }
59 }
60 Some(_) => self.parse_identifier()?,
61 };
62
63 self.skip_whitespace();
64
65 match self.it.peek() {
66 Some('{') => {
67 self.it.next();
68 children.insert(identifier, ConfigNodeValue::Node(self.parse_confignode()?));
69 self.skip_whitespace();
70
71 match self.it.next() {
72 Some('}') => {}
73 Some(_) => {
74 return Err(ParseError {
76 kind: ParseErrorKind::InvalidCharacter,
77 });
78 }
79 None => {
80 return Err(ParseError {
81 kind: ParseErrorKind::UnexpectedEof,
82 })
83 }
84 }
85 }
86 Some('=') => {
87 self.it.next();
88 children.insert(identifier, ConfigNodeValue::Text(self.parse_string()?));
89 }
90 Some(_) => {
91 return Err(ParseError {
93 kind: ParseErrorKind::InvalidCharacter,
94 });
95 }
96 None => {
97 return Err(ParseError {
98 kind: ParseErrorKind::UnexpectedEof,
99 })
100 }
101 }
102
103 self.skip_whitespace();
104 }
105
106 Ok(ConfigNode { children })
107 }
108
109 fn parse_identifier(&mut self) -> Result<String, ParseError> {
110 let mut identifier = String::new();
111
112 loop {
113 match self.it.peek() {
114 Some('{') | Some('=') | Some('\n') | Some('\r') => {
115 break;
116 }
117 Some('/') => {
118 self.it.next();
119
120 if self.it.peek() == Some(&'/') {
121 self.skip_line();
122 break;
123 } else {
124 identifier.push('/');
125 }
126 }
127 Some('}') => {
128 return Err(ParseError {
129 kind: ParseErrorKind::InvalidCharacter,
130 })
131 }
132 Some(c) => {
133 identifier.push(*c);
134 self.it.next();
135 }
136 None => {
137 return Err(ParseError {
138 kind: ParseErrorKind::UnexpectedEof,
139 })
140 }
141 }
142 }
143
144 Ok(identifier.trim().to_owned())
145 }
146
147 fn parse_string(&mut self) -> Result<String, ParseError> {
148 let mut string = String::new();
149
150 while let Some(c) = self.it.peek() {
151 if *c == '\n' || *c == '\r' {
152 break;
153 } else if *c == '/' {
154 self.it.next();
155
156 if self.it.peek() == Some(&'/') {
157 self.skip_line();
158 break;
159 } else {
160 string.push('/');
161 }
162 } else {
163 string.push(*c);
164 self.it.next();
165 }
166 }
167
168 Ok(string.trim().to_owned())
169 }
170
171 fn skip_line(&mut self) {
172 while self.it.peek().map_or(false, |&x| x != '\n') {
173 self.it.next();
174 }
175 }
176
177 fn skip_whitespace(&mut self) {
178 while self.it.peek().map_or(false, |x| x.is_ascii_whitespace()) {
179 self.it.next();
180 }
181 }
182}
183
184impl ConfigNodeValue {
185 pub fn as_text(&self) -> Option<&str> {
188 match self {
189 ConfigNodeValue::Text(x) => Some(x),
190 ConfigNodeValue::Node(_) => None,
191 }
192 }
193
194 pub fn as_node(&self) -> Option<&ConfigNode> {
197 match self {
198 ConfigNodeValue::Text(_) => None,
199 ConfigNodeValue::Node(x) => Some(x),
200 }
201 }
202}