1use std::collections::HashMap;
2
3use crate::combinators::attribute;
4use crate::error::VdfError;
5
6mod combinators;
7pub mod error;
8
9#[derive(Debug, PartialEq)]
10pub enum VdfValue {
11 String(String),
12 Block(HashMap<String, VdfAttribute>),
13}
14
15impl From<VdfValue> for String {
16 fn from(value: VdfValue) -> Self {
17 match value {
18 VdfValue::String(string) => string,
19 VdfValue::Block(_) => panic!("Cannot convert VdfValue::Block to String"),
20 }
21 }
22}
23
24#[derive(Debug, PartialEq)]
25pub struct VdfAttribute {
26 pub comments_before: Vec<String>,
27 pub comment_after: Option<String>,
28 pub key: String,
29 pub value: VdfValue,
30}
31
32impl VdfValue {}
33
34impl VdfAttribute {
35 pub fn get_string_value(&self, key: &str) -> Result<&VdfAttribute, VdfError> {
36 match &self.value {
37 VdfValue::Block(block) => match block.get(key) {
38 Some(value) => Ok(value),
39 None => Err(VdfError::ValueNotFound(
40 key.to_string(),
41 self.key.to_string(),
42 )),
43 },
44 _ => Err(VdfError::ValueNotFound(
45 key.to_string(),
46 self.key.to_string(),
47 )),
48 }
49 }
50}
51
52pub fn parse_vdf_text(text: &str) -> Result<VdfAttribute, VdfError> {
57 let (_, vdf) = attribute(text).expect("Failed to parse vdf file");
58 Ok(vdf)
59}
60
61#[cfg(test)]
62mod tests {
63 use std::collections::HashMap;
64
65 use crate::{parse_vdf_text, VdfAttribute, VdfValue};
66
67 #[test]
68 fn it_works() {
69 {
70 let input = r#"
71 // Comment at the start of the file
72 "default_attack"
73 {
74 // Comment before a key-value pair 1
75 // Comment before a key-value pair 2
76 // Comment before a key-value pair 3
77 "ID" "5001"
78 "Damage" "100" // Comment after a key-value pair
79
80 "test_block"
81 {
82 "test_key" "test_value"
83 }
84 }
85 // Comment at the end of the file
86 "#;
87
88 let result = parse_vdf_text(input);
89 let expected = VdfAttribute {
90 key: "default_attack".to_string(),
91 comments_before: vec!["Comment at the start of the file".to_string()],
92 comment_after: Some("Comment at the end of the file".to_string()),
93 value: VdfValue::Block(HashMap::from([
94 (
95 "Damage".to_string(),
96 VdfAttribute {
97 key: "Damage".to_string(),
98 comments_before: vec![],
99 comment_after: Some("Comment after a key-value pair".to_string()),
100 value: VdfValue::String("100".to_string()),
101 },
102 ),
103 (
104 "ID".to_string(),
105 VdfAttribute {
106 key: "ID".to_string(),
107 comments_before: vec![
108 "Comment before a key-value pair 1".to_string(),
109 "Comment before a key-value pair 2".to_string(),
110 "Comment before a key-value pair 3".to_string(),
111 ],
112 comment_after: None,
113 value: VdfValue::String("5001".to_string()),
114 },
115 ),
116 (
117 "test_block".to_string(),
118 VdfAttribute {
119 key: "test_block".to_string(),
120 comments_before: vec![],
121 comment_after: None,
122 value: VdfValue::Block(HashMap::from([(
123 "test_key".to_string(),
124 VdfAttribute {
125 key: "test_key".to_string(),
126 comments_before: vec![],
127 comment_after: None,
128 value: VdfValue::String("test_value".to_string()),
129 },
130 )])),
131 },
132 ),
133 ])),
134 };
135 assert!(result.is_ok(), "Failed to parse attribute");
136 let result = result.unwrap();
137 assert_eq!(result.key, expected.key, "Parsed key does not match");
138 assert_eq!(result.value, expected.value, "Parsed value does not match");
139 }
140 }
141
142 #[test]
143 fn can_parse_multiple_nested_blocks() {
144 let input = r#"
145 "default_attack"
146 {
147 // Comment before a key-value pair 1
148 // Comment before a key-value pair 2
149 // Comment before a key-value pair 3
150 "ID" "5001"
151 "Damage" "100" // Comment after a key-value pair
152 "test_block_a"
153 {
154 "test_key" "test_value"
155
156 "test_block_a_a"
157 {
158 "test_key2" "test_value2"
159 }
160 }
161 "test_block_b"
162 {
163 "test_key2" "test_value2"
164 }
165 }
166 "#;
167
168 let result = parse_vdf_text(input);
169 assert!(result.is_ok(), "Failed to parse attribute");
170 }
171
172 #[test]
173 fn can_parse_block_with_only_comments() {
174 let input = r#"
175 "default_attack"
176 {
177 "ID" "5001"
178 "Damage" "100"
179 "test_block_a"
180 {
181 "test_key" "test_value"
182
183 "test_block_a_a"
184 {
185 //"test_key2" "test_value2"
186 }
187 }
188 "test_block_b"
189 {
190 "test_key2" "test_value2"
191 }
192 }
193 "#;
194 let result = parse_vdf_text(input);
195 assert!(result.is_ok(), "Failed to parse attribute");
196 }
197
198 #[test]
199 fn can_parse_value_with_spaces() {
200 let input = r#"
201 "default_attack"
202 {
203 "ID" "5001"
204 "Damage" "100"
205 "test_block"
206 {
207 "test_key" "0 0 0 0 0"
208 }
209 }
210 "#;
211
212 let expected = VdfAttribute {
213 key: "default_attack".to_string(),
214 comments_before: vec![],
215 comment_after: None,
216 value: VdfValue::Block(HashMap::from([
217 (
218 "Damage".to_string(),
219 VdfAttribute {
220 key: "Damage".to_string(),
221 comments_before: vec![],
222 comment_after: None,
223 value: VdfValue::String("100".to_string()),
224 },
225 ),
226 (
227 "ID".to_string(),
228 VdfAttribute {
229 key: "ID".to_string(),
230 comments_before: vec![],
231 comment_after: None,
232 value: VdfValue::String("5001".to_string()),
233 },
234 ),
235 (
236 "test_block".to_string(),
237 VdfAttribute {
238 key: "test_block".to_string(),
239 comments_before: vec![],
240 comment_after: None,
241 value: VdfValue::Block(HashMap::from([(
242 "test_key".to_string(),
243 VdfAttribute {
244 key: "test_key".to_string(),
245 comments_before: vec![],
246 comment_after: None,
247 value: VdfValue::String("0 0 0 0 0".to_string()),
248 },
249 )])),
250 },
251 ),
252 ])),
253 };
254 let result = parse_vdf_text(input);
255 assert!(result.is_ok(), "Failed to parse attribute");
256 let result = result.unwrap();
257 assert_eq!(result, expected, "Parsed value does not match");
258 println!("Result {:#?}", result);
259 println!("Expected {:#?}", expected);
260 }
261}