bellande_format/
bellande_parser.rs1use std::collections::HashMap;
17use std::fs;
18use std::path::Path;
19
20#[derive(Debug, Clone)]
21pub enum BellandeValue {
22 String(String),
23 Integer(i64),
24 Float(f64),
25 Boolean(bool),
26 Null,
27 List(Vec<BellandeValue>),
28 Map(HashMap<String, BellandeValue>),
29}
30
31pub struct BellandeFormat;
32
33impl BellandeFormat {
34 pub fn parse_bellande<P: AsRef<Path>>(
35 &self,
36 file_path: P,
37 ) -> Result<BellandeValue, std::io::Error> {
38 let content = fs::read_to_string(file_path)?;
39 let lines: Vec<&str> = content.lines().collect();
40 let parsed_data = self.parse_lines(&lines);
41 Ok(parsed_data)
42 }
43
44 pub fn parse_lines(&self, lines: &[&str]) -> BellandeValue {
45 let mut root = BellandeValue::Map(HashMap::new());
46 let mut stack: Vec<(usize, String)> = vec![(0, String::new())];
47
48 for line in lines {
49 let stripped = line.trim();
50 if stripped.is_empty() || stripped.starts_with('#') {
51 continue;
52 }
53
54 let indent = line.len() - stripped.len();
55
56 while let Some(&(last_indent, _)) = stack.last() {
57 if indent <= last_indent {
58 stack.pop();
59 } else {
60 break;
61 }
62 }
63
64 if let Some(colon_pos) = stripped.find(':') {
65 let (key, value) = stripped.split_at(colon_pos);
66 let key = key.trim().to_string();
67 let value = value[1..].trim();
68
69 if !value.is_empty() {
70 let parsed_value = self.parse_value(value);
71 self.insert_value(&mut root, &stack, &key, parsed_value);
72 } else {
73 let new_list = BellandeValue::List(Vec::new());
74 self.insert_value(&mut root, &stack, &key, new_list);
75 stack.push((indent, key));
76 }
77 } else if stripped.starts_with('-') {
78 let value = stripped[1..].trim();
79 let parsed_value = self.parse_value(value);
80 if let Some((_, key)) = stack.last() {
81 self.append_to_list(&mut root, &stack, key, parsed_value);
82 }
83 }
84 }
85
86 root
87 }
88
89 pub fn insert_value(
90 &self,
91 root: &mut BellandeValue,
92 stack: &[(usize, String)],
93 key: &str,
94 value: BellandeValue,
95 ) {
96 let mut current = root;
97 for (_, path_key) in stack.iter().skip(1) {
98 if let BellandeValue::Map(map) = current {
99 current = map.get_mut(path_key).unwrap();
100 }
101 }
102 if let BellandeValue::Map(map) = current {
103 map.insert(key.to_string(), value);
104 }
105 }
106
107 pub fn append_to_list(
108 &self,
109 root: &mut BellandeValue,
110 stack: &[(usize, String)],
111 key: &str,
112 value: BellandeValue,
113 ) {
114 let mut current = root;
115 for (_, path_key) in stack.iter().skip(1) {
116 if let BellandeValue::Map(map) = current {
117 current = map.get_mut(path_key).unwrap();
118 }
119 }
120 if let BellandeValue::Map(map) = current {
121 if let Some(BellandeValue::List(list)) = map.get_mut(key) {
122 list.push(value);
123 }
124 }
125 }
126
127 pub fn parse_value(&self, value: &str) -> BellandeValue {
128 if value.eq_ignore_ascii_case("true") {
129 BellandeValue::Boolean(true)
130 } else if value.eq_ignore_ascii_case("false") {
131 BellandeValue::Boolean(false)
132 } else if value.eq_ignore_ascii_case("null") {
133 BellandeValue::Null
134 } else if value.starts_with('"') && value.ends_with('"') {
135 BellandeValue::String(value[1..value.len() - 1].to_string())
136 } else if let Ok(int_value) = value.parse::<i64>() {
137 BellandeValue::Integer(int_value)
138 } else if let Ok(float_value) = value.parse::<f64>() {
139 BellandeValue::Float(float_value)
140 } else {
141 BellandeValue::String(value.to_string())
142 }
143 }
144
145 pub fn write_bellande<P: AsRef<Path>>(
146 &self,
147 data: &BellandeValue,
148 file_path: P,
149 ) -> Result<(), std::io::Error> {
150 let content = self.to_bellande_string(data, 0);
151 fs::write(file_path, content)
152 }
153
154 pub fn to_bellande_string(&self, data: &BellandeValue, indent: usize) -> String {
155 match data {
156 BellandeValue::Map(map) => map
157 .iter()
158 .map(|(key, value)| {
159 let value_str = match value {
160 BellandeValue::Map(_) | BellandeValue::List(_) => {
161 format!("\n{}", self.to_bellande_string(value, indent + 2))
162 }
163 _ => format!(" {}", self.format_value(value)),
164 };
165 format!("{}{}: {}", " ".repeat(indent), key, value_str)
166 })
167 .collect::<Vec<_>>()
168 .join("\n"),
169 BellandeValue::List(list) => list
170 .iter()
171 .map(|item| {
172 format!(
173 "{}- {}",
174 " ".repeat(indent),
175 self.to_bellande_string(item, indent + 2)
176 )
177 })
178 .collect::<Vec<_>>()
179 .join("\n"),
180 _ => self.format_value(data),
181 }
182 }
183
184 pub fn format_value(&self, value: &BellandeValue) -> String {
185 match value {
186 BellandeValue::String(s) => {
187 if s.contains(' ')
188 || s.contains(':')
189 || ["true", "false", "null"].contains(&s.to_lowercase().as_str())
190 {
191 format!("\"{}\"", s)
192 } else {
193 s.clone()
194 }
195 }
196 BellandeValue::Integer(i) => i.to_string(),
197 BellandeValue::Float(f) => f.to_string(),
198 BellandeValue::Boolean(b) => b.to_string().to_lowercase(),
199 BellandeValue::Null => "null".to_string(),
200 BellandeValue::List(_) | BellandeValue::Map(_) => unreachable!(),
201 }
202 }
203}