steam_vdf_parser/value/
types.rs

1//! Type definitions for VDF values.
2
3use alloc::borrow::Cow;
4use hashbrown::HashMap;
5
6/// A key in VDF - zero-copy when possible
7pub(crate) type Key<'text> = Cow<'text, str>;
8
9/// VDF Value - can be a string, number, object, or other types
10#[derive(Clone, Debug, PartialEq)]
11pub enum Value<'text> {
12    /// A string value (text format and WideString from binary)
13    Str(Cow<'text, str>),
14    /// An object containing nested key-value pairs
15    Obj(Obj<'text>),
16    /// A 32-bit signed integer (binary Int32 type)
17    I32(i32),
18    /// A 64-bit unsigned integer (binary UInt64 type)
19    U64(u64),
20    /// A 32-bit float (binary Float type)
21    Float(f32),
22    /// A pointer value (binary Ptr type, stored as u32)
23    Pointer(u32),
24    /// A color value (binary Color type, RGBA)
25    Color([u8; 4]),
26}
27
28impl<'text> Value<'text> {
29    /// Returns `true` if this value is a string.
30    pub fn is_str(&self) -> bool {
31        matches!(self, Value::Str(_))
32    }
33
34    /// Returns `true` if this value is an object.
35    pub fn is_obj(&self) -> bool {
36        matches!(self, Value::Obj(_))
37    }
38
39    /// Returns `true` if this value is an i32.
40    pub fn is_i32(&self) -> bool {
41        matches!(self, Value::I32(_))
42    }
43
44    /// Returns `true` if this value is a u64.
45    pub fn is_u64(&self) -> bool {
46        matches!(self, Value::U64(_))
47    }
48
49    /// Returns `true` if this value is a float.
50    pub fn is_float(&self) -> bool {
51        matches!(self, Value::Float(_))
52    }
53
54    /// Returns `true` if this value is a pointer.
55    pub fn is_pointer(&self) -> bool {
56        matches!(self, Value::Pointer(_))
57    }
58
59    /// Returns `true` if this value is a color.
60    pub fn is_color(&self) -> bool {
61        matches!(self, Value::Color(_))
62    }
63
64    /// Returns a reference to the string value if this is a string.
65    pub fn as_str(&self) -> Option<&str> {
66        match self {
67            Value::Str(s) => Some(s.as_ref()),
68            _ => None,
69        }
70    }
71
72    /// Returns a reference to the object if this is an object.
73    pub fn as_obj(&self) -> Option<&Obj<'text>> {
74        match self {
75            Value::Obj(obj) => Some(obj),
76            _ => None,
77        }
78    }
79
80    /// Returns a mutable reference to the object if this is an object.
81    pub fn as_obj_mut(&mut self) -> Option<&mut Obj<'text>> {
82        match self {
83            Value::Obj(obj) => Some(obj),
84            _ => None,
85        }
86    }
87
88    /// Returns the i32 value if this is an i32.
89    pub fn as_i32(&self) -> Option<i32> {
90        match self {
91            Value::I32(n) => Some(*n),
92            _ => None,
93        }
94    }
95
96    /// Returns the u64 value if this is a u64.
97    pub fn as_u64(&self) -> Option<u64> {
98        match self {
99            Value::U64(n) => Some(*n),
100            _ => None,
101        }
102    }
103
104    /// Returns the float value if this is a float.
105    pub fn as_float(&self) -> Option<f32> {
106        match self {
107            Value::Float(n) => Some(*n),
108            _ => None,
109        }
110    }
111
112    /// Returns the pointer value if this is a pointer.
113    pub fn as_pointer(&self) -> Option<u32> {
114        match self {
115            Value::Pointer(n) => Some(*n),
116            _ => None,
117        }
118    }
119
120    /// Returns the color value if this is a color.
121    pub fn as_color(&self) -> Option<[u8; 4]> {
122        match self {
123            Value::Color(c) => Some(*c),
124            _ => None,
125        }
126    }
127
128    /// Returns a reference to a nested value by key.
129    ///
130    /// Shorthand for `self.as_obj()?.get(key)`.
131    pub fn get(&self, key: &str) -> Option<&Value<'text>> {
132        self.as_obj()?.get(key)
133    }
134
135    /// Traverse nested objects by path.
136    ///
137    /// Returns `None` if any segment doesn't exist or isn't an object.
138    pub fn get_path(&self, path: &[&str]) -> Option<&Value<'text>> {
139        let mut current = self;
140        for key in path {
141            current = current.get(key)?;
142        }
143        Some(current)
144    }
145
146    /// Get a string at the given path.
147    pub fn get_str(&self, path: &[&str]) -> Option<&str> {
148        self.get_path(path)?.as_str()
149    }
150
151    /// Get an object at the given path.
152    pub fn get_obj(&self, path: &[&str]) -> Option<&Obj<'text>> {
153        self.get_path(path)?.as_obj()
154    }
155
156    /// Get an i32 at the given path.
157    pub fn get_i32(&self, path: &[&str]) -> Option<i32> {
158        self.get_path(path)?.as_i32()
159    }
160
161    /// Get a u64 at the given path.
162    pub fn get_u64(&self, path: &[&str]) -> Option<u64> {
163        self.get_path(path)?.as_u64()
164    }
165
166    /// Get a float at the given path.
167    pub fn get_float(&self, path: &[&str]) -> Option<f32> {
168        self.get_path(path)?.as_float()
169    }
170}
171
172/// Object - map from keys to values
173///
174/// Uses `HashMap` for O(1) lookup. Binary VDF doesn't have duplicate keys,
175/// and for text VDF we use "last value wins" semantics.
176#[derive(Clone, Debug, PartialEq)]
177pub struct Obj<'text> {
178    pub(crate) inner: HashMap<Key<'text>, Value<'text>>,
179}
180
181impl<'text> Obj<'text> {
182    /// Creates a new empty VDF object.
183    pub fn new() -> Self {
184        Self {
185            inner: HashMap::new(),
186        }
187    }
188
189    /// Returns the number of key-value pairs in the object.
190    pub fn len(&self) -> usize {
191        self.inner.len()
192    }
193
194    /// Returns `true` if the object contains no key-value pairs.
195    pub fn is_empty(&self) -> bool {
196        self.inner.is_empty()
197    }
198
199    /// Returns a reference to the value corresponding to the key.
200    pub fn get(&self, key: &str) -> Option<&Value<'text>> {
201        self.inner.get(key)
202    }
203
204    /// Returns an iterator over the key-value pairs.
205    pub fn iter(&self) -> impl Iterator<Item = (&Key<'text>, &Value<'text>)> {
206        self.inner.iter()
207    }
208
209    /// Returns an iterator over the keys.
210    pub fn keys(&self) -> impl Iterator<Item = &str> {
211        self.inner.keys().map(|k| k.as_ref())
212    }
213
214    /// Returns an iterator over the values.
215    pub fn values(&self) -> impl Iterator<Item = &Value<'text>> {
216        self.inner.values()
217    }
218
219    /// Returns `true` if the object contains the given key.
220    pub fn contains_key(&self, key: &str) -> bool {
221        self.inner.contains_key(key)
222    }
223
224    /// Returns a mutable reference to the value corresponding to the key.
225    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value<'text>> {
226        self.inner.get_mut(key)
227    }
228
229    /// Inserts a key-value pair into the object.
230    ///
231    /// Returns the previous value if one existed for this key.
232    pub fn insert(
233        &mut self,
234        key: impl Into<Key<'text>>,
235        value: Value<'text>,
236    ) -> Option<Value<'text>> {
237        self.inner.insert(key.into(), value)
238    }
239
240    /// Removes a key from the object.
241    ///
242    /// Returns the value if the key was present.
243    pub fn remove(&mut self, key: &str) -> Option<Value<'text>> {
244        self.inner.remove(key)
245    }
246}
247
248/// Top-level VDF document
249///
250/// A VDF document is essentially a single key-value pair at the root level.
251#[derive(Clone, Debug, PartialEq)]
252pub struct Vdf<'text> {
253    key: Key<'text>,
254    value: Value<'text>,
255}
256
257impl<'text> Vdf<'text> {
258    /// Creates a new VDF document.
259    pub fn new(key: impl Into<Key<'text>>, value: Value<'text>) -> Self {
260        Self {
261            key: key.into(),
262            value,
263        }
264    }
265
266    /// Returns the root key.
267    pub fn key(&self) -> &str {
268        &self.key
269    }
270
271    /// Returns a reference to the root value.
272    pub fn value(&self) -> &Value<'text> {
273        &self.value
274    }
275
276    /// Returns a mutable reference to the root value.
277    pub fn value_mut(&mut self) -> &mut Value<'text> {
278        &mut self.value
279    }
280
281    /// Consumes the Vdf and returns its parts.
282    pub fn into_parts(self) -> (Cow<'text, str>, Value<'text>) {
283        (self.key, self.value)
284    }
285
286    /// Returns `true` if the root value is an object.
287    pub fn is_obj(&self) -> bool {
288        self.value.is_obj()
289    }
290
291    /// Returns a reference to the root object if it is one.
292    pub fn as_obj(&self) -> Option<&Obj<'text>> {
293        self.value.as_obj()
294    }
295
296    /// Returns a reference to a nested value by key.
297    pub fn get(&self, key: &str) -> Option<&Value<'text>> {
298        self.value.get(key)
299    }
300
301    /// Traverse nested objects by path from the root value.
302    pub fn get_path(&self, path: &[&str]) -> Option<&Value<'text>> {
303        self.value.get_path(path)
304    }
305
306    /// Get a string at the given path.
307    pub fn get_str(&self, path: &[&str]) -> Option<&str> {
308        self.value.get_str(path)
309    }
310
311    /// Get an object at the given path.
312    pub fn get_obj(&self, path: &[&str]) -> Option<&Obj<'text>> {
313        self.value.get_obj(path)
314    }
315
316    /// Get an i32 at the given path.
317    pub fn get_i32(&self, path: &[&str]) -> Option<i32> {
318        self.value.get_i32(path)
319    }
320
321    /// Get a u64 at the given path.
322    pub fn get_u64(&self, path: &[&str]) -> Option<u64> {
323        self.value.get_u64(path)
324    }
325
326    /// Get a float at the given path.
327    pub fn get_float(&self, path: &[&str]) -> Option<f32> {
328        self.value.get_float(path)
329    }
330}