1use core::iter::Iterator;
3use std::collections::HashMap;
4use std::fmt;
5
6#[derive(Debug)]
7pub struct ParserError {
9 pub msg: String,
10 pub line: Option<usize>,
11}
12
13impl fmt::Display for ParserError {
14 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15 if let Some(num) = self.line {
16 write!(f, "{} at line '{num}'", self.msg)?
17 } else {
18 write!(f, "{}", self.msg)?
19 }
20 Ok(())
21 }
22}
23
24impl std::error::Error for ParserError {}
25
26#[derive(Debug)]
29pub struct TagSection {
30 data: HashMap<String, String>,
31}
32
33impl TagSection {
34 fn error(msg: &str, line: Option<usize>) -> Result<Self, ParserError> {
35 Err(ParserError {
36 msg: "E:".to_owned() + msg,
37 line,
38 })
39 }
40
41 fn line_is_key(line: &str) -> bool { !line.starts_with(' ') && !line.starts_with('\t') }
42
43 fn next_line_extends_value(lines: &[&str], current_line: usize) -> bool {
44 if let Some(next_line) = lines.get(current_line + 1) {
45 !Self::line_is_key(next_line)
46 } else {
47 false
48 }
49 }
50
51 pub fn new(section: &str) -> Result<Self, ParserError> {
56 if section.contains("\n\n") {
58 return Self::error("More than one section was found", None);
59 }
60
61 if section.is_empty() {
63 return Self::error("An empty string was passed", None);
64 }
65
66 let mut data = HashMap::new();
68 let lines = section.lines().collect::<Vec<&str>>();
69
70 let mut current_key: Option<String> = None;
72 let mut current_value = String::new();
73
74 for (index, line) in lines.iter().enumerate() {
75 let line_number = index + 1;
77
78 if line.starts_with('#') {
80 continue;
81 }
82
83 if Self::line_is_key(line) {
85 let (key, value) = match line.split_once(':') {
86 Some((key, value)) => {
87 (key.to_string(), value.strip_prefix(' ').unwrap_or(value))
88 },
89 None => {
90 return Self::error(
91 "Line doesn't contain a ':' separator",
92 Some(line_number),
93 );
94 },
95 };
96
97 current_key = Some(key);
105
106 if value.is_empty() {
107 current_value = "\n".to_string();
108 } else {
109 current_value = value.to_string();
110
111 if Self::next_line_extends_value(&lines, index) {
113 current_value += "\n";
114 }
115 }
116 }
117
118 if line.starts_with(' ') || line.starts_with('\t') {
121 current_value += line;
122
123 if Self::next_line_extends_value(&lines, index) {
126 current_value += "\n";
127 }
128 }
129
130 if !Self::next_line_extends_value(&lines, index) {
134 if current_key.is_none() {
139 return Self::error(
140 "No key defined for the currently indented line",
141 Some(line_number),
142 );
143 }
144
145 data.insert(current_key.unwrap(), current_value);
147 current_key = None;
148 current_value = String::new();
149 }
150 }
151
152 Ok(Self { data })
153 }
154
155 pub fn hashmap(&self) -> &HashMap<String, String> { &self.data }
157
158 pub fn get(&self, key: &str) -> Option<&String> { self.data.get(key) }
160
161 pub fn get_default<'a, 'b: 'a>(&'a self, key: &str, default: &'b str) -> &str {
165 if let Some(value) = self.data.get(key) {
166 return value;
167 }
168 default
169 }
170}
171
172pub fn parse_tagfile(content: &str) -> Result<Vec<TagSection>, ParserError> {
180 let mut sections = vec![];
181 let section_strings = content.split("\n\n");
182
183 for (iter, section) in section_strings.clone().enumerate() {
184 if section.is_empty() || section.chars().all(|c| c == '\n') {
187 break;
188 }
189
190 match TagSection::new(section) {
191 Ok(section) => sections.push(section),
192 Err(mut err) => {
193 let mut line_count = 0;
198
199 for _ in 0..iter {
200 line_count += 1;
202
203 line_count += section_strings.clone().count();
205 }
206
207 if let Some(line) = err.line {
208 err.line = Some(line_count + line);
209 } else {
210 err.line = Some(line_count);
211 }
212 },
213 }
214 }
215
216 Ok(sections)
217}