mirror of
https://codeberg.org/icewind/vdf-reader.git
synced 2026-06-03 10:04:08 +02:00
untagged enums
This commit is contained in:
parent
08974c9db9
commit
08e7d35905
5 changed files with 99 additions and 9 deletions
39
README.md
39
README.md
|
|
@ -3,3 +3,42 @@
|
|||
A parser for Valve's Data Format v1 (VDF) also known as [KeyValues](https://developer.valvesoftware.com/wiki/KeyValues).
|
||||
|
||||
The parser focuses on being able to deal with all the various weird forms vdf takes in the wild and providing access to the data stream instead of always requiring parsing the file in full.
|
||||
|
||||
## Serde
|
||||
|
||||
This crate implements a deserializer for serde, but because VDF doesn't map that well only the serde data model not every type might deserialize properly.
|
||||
|
||||
### Limitations
|
||||
|
||||
- Because the boolean values `0` and `1` can't be distinguished from numbers, it is not possible to use booleans in untagged enums.
|
||||
|
||||
|
||||
### Tagged enum root
|
||||
|
||||
To help deserialize some common vdf formats, you can use a tagged enum as the root element instead of a struct.
|
||||
|
||||
```vdf
|
||||
"Variant1" {
|
||||
content 1
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```vdf
|
||||
"Variant2" {
|
||||
other foo
|
||||
}
|
||||
```
|
||||
|
||||
can be deserialized into a
|
||||
```rust
|
||||
enum Data {
|
||||
Variant1 {
|
||||
content: bool,
|
||||
},
|
||||
Variant2 {
|
||||
other: String,
|
||||
}
|
||||
}
|
||||
```
|
||||
51
src/serde.rs
51
src/serde.rs
|
|
@ -94,14 +94,36 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
|||
V: Visitor<'de>,
|
||||
{
|
||||
let source = self.source();
|
||||
let peek = self.peek().expect_token(VALUE_TOKEN, source)?;
|
||||
match peek.token {
|
||||
Token::Item | Token::QuotedItem | Token::Statement | Token::QuotedStatement => self
|
||||
.deserialize_str(visitor)
|
||||
.ensure_span(peek.span, self.source()),
|
||||
Token::GroupStart => self
|
||||
.deserialize_map(visitor)
|
||||
.ensure_span(peek.span, self.source()),
|
||||
let token = self.next().expect_token(VALUE_TOKEN, source)?;
|
||||
let span = token.span.clone();
|
||||
match token.token {
|
||||
Token::Item | Token::QuotedItem | Token::Statement | Token::QuotedStatement => {
|
||||
let str = token.string(self.source());
|
||||
// note: we don't check for bool as we can't distinguish those from numbers
|
||||
if let Ok(int) = i64::from_str(str.as_ref()) {
|
||||
return visitor.visit_i64(int).ensure_span(span, self.source());
|
||||
}
|
||||
if let Ok(float) = f64::from_str(str.as_ref()) {
|
||||
return visitor.visit_f64(float).ensure_span(span, self.source());
|
||||
}
|
||||
if str.starts_with('[') && str.ends_with(']') {
|
||||
self.push_peeked(token);
|
||||
return self
|
||||
.deserialize_seq(visitor)
|
||||
.ensure_span(span, self.source());
|
||||
}
|
||||
match str {
|
||||
Cow::Borrowed(str) => visitor
|
||||
.visit_borrowed_str(str)
|
||||
.ensure_span(span, self.source()),
|
||||
Cow::Owned(str) => visitor.visit_string(str).ensure_span(span, self.source()),
|
||||
}
|
||||
}
|
||||
Token::GroupStart => {
|
||||
self.push_peeked(token);
|
||||
self.deserialize_map(visitor)
|
||||
.ensure_span(span, self.source())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -780,4 +802,17 @@ mod tests {
|
|||
let expected = E::Struct2 { a: 1 };
|
||||
assert_eq!(expected, unwrap_err(from_str(j)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untagged_enum() {
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
enum E {
|
||||
Int(u8),
|
||||
Float(f32),
|
||||
}
|
||||
|
||||
let j = r#"1.1"#;
|
||||
assert_eq!(E::Float(1.1), unwrap_err(from_str(j)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,7 @@
|
|||
fixed_array "[1 2 3]"
|
||||
flex_array "[1.0 2.2]"
|
||||
tuple "[1 57]"
|
||||
single 1.2
|
||||
triple "[1.2 1.3 1.4]"
|
||||
single_int 2
|
||||
}
|
||||
|
|
@ -11,6 +11,9 @@ enum Expected {
|
|||
fixed_array: [u8; 3],
|
||||
flex_array: Vec<f32>,
|
||||
tuple: (bool, u8),
|
||||
single: SingleOrTriple<f32>,
|
||||
triple: SingleOrTriple<f32>,
|
||||
single_int: SingleOrTriple<f32>,
|
||||
},
|
||||
LightmappedGeneric {
|
||||
#[serde(rename = "$baseTexture")]
|
||||
|
|
@ -52,6 +55,13 @@ enum Expected {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum SingleOrTriple<T> {
|
||||
Single(T),
|
||||
Triple([T; 3]),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct UserConfigDataSteam {
|
||||
cached: UserConfigDataSteamCached,
|
||||
|
|
|
|||
|
|
@ -9,4 +9,7 @@ Types(
|
|||
2.2,
|
||||
],
|
||||
tuple: (true, 57),
|
||||
single: 1.2,
|
||||
triple: (1.2, 1.3, 1.4),
|
||||
single_int: 2.0,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue