vdf_parser/
lib.rs

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
52/// ## Parse a VDF text
53/// ______
54/// - `text` - The VDF text to parse
55/// - `return` - A `Result` containing either a `VdfAttribute` or a `VdfError`
56pub 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}