bellande_format/
bellande_parser.rs

1// Copyright (C) 2024 Bellande Algorithm Model Research Innovation Center, Ronaldson Bellande
2
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16use 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}