Obj

Struct Obj 

Source
pub struct Obj<'text> { /* private fields */ }
Expand description

Object - map from keys to values

Uses IndexMap for O(1) lookup while preserving insertion order. Binary VDF doesn’t have duplicate keys, and for text VDF we use “last value wins” semantics.

Implementations§

Source§

impl<'text> Obj<'text>

Source

pub fn new() -> Self

Creates a new empty VDF object.

Source

pub fn len(&self) -> usize

Returns the number of key-value pairs in the object.

Examples found in repository?
examples/verify_compactified.rs (line 31)
6fn main() {
7    let args: Vec<String> = env::args().collect();
8
9    if args.len() < 2 {
10        eprintln!("Usage: {} <vdf_file>", args[0]);
11        std::process::exit(1);
12    }
13
14    let path = &args[1];
15
16    // Read the file
17    let data = match std::fs::read(path) {
18        Ok(d) => d,
19        Err(e) => {
20            eprintln!("Error reading file: {}", e);
21            std::process::exit(1);
22        }
23    };
24
25    // Parse it
26    match parse_appinfo(&data) {
27        Ok(vdf) => {
28            println!("Successfully parsed {}", path);
29            println!("Root key: {}", vdf.key());
30            if let Some(obj) = vdf.as_obj() {
31                println!("Number of apps: {}", obj.len());
32                for (key, _) in obj.iter().take(5) {
33                    println!("  - app_id: {}", key);
34                }
35                if obj.len() > 5 {
36                    println!("  ... and {} more", obj.len() - 5);
37                }
38            }
39        }
40        Err(e) => {
41            eprintln!("Error parsing file: {:?}", e);
42            std::process::exit(1);
43        }
44    }
45}
More examples
Hide additional examples
examples/test_parse_real.rs (line 16)
4fn main() {
5    // Test localconfig.vdf (text format)
6    println!("=== Parsing localconfig.vdf (text format) ===");
7    let localconfig = fs::read_to_string(
8        "/home/mexus/.local/share/Steam/userdata/127648749/config/localconfig.vdf",
9    );
10    match localconfig {
11        Ok(content) => match parse_text(&content) {
12            Ok(vdf) => {
13                println!("Success!");
14                println!("Root key: {}", vdf.key());
15                let obj = vdf.as_obj().unwrap();
16                println!("Root has {} keys", obj.len());
17            }
18            Err(e) => {
19                println!("Parse error: {:?}", e);
20            }
21        },
22        Err(e) => println!("Error reading: {}", e),
23    }
24
25    println!("\n=== Parsing appinfo.vdf (binary format) ===");
26    let appinfo = fs::read("/home/mexus/.local/share/Steam/appcache/appinfo.vdf");
27    match appinfo {
28        Ok(data) => match parse_binary(&data) {
29            Ok(vdf) => {
30                println!("Success!");
31                println!("Root key: {}", vdf.key());
32                let obj = vdf.as_obj().unwrap();
33                println!("Root has {} keys", obj.len());
34            }
35            Err(e) => {
36                println!("Parse error: {:?}", e);
37            }
38        },
39        Err(e) => println!("Error reading: {}", e),
40    }
41}
Source

pub fn is_empty(&self) -> bool

Returns true if the object contains no key-value pairs.

Source

pub fn get(&self, key: &str) -> Option<&Value<'text>>

Returns a reference to the value corresponding to the key.

Examples found in repository?
examples/appid_to_name.rs (line 77)
17fn main() -> ExitCode {
18    let args: Vec<String> = env::args().collect();
19
20    if args.len() < 2 {
21        eprintln!("Usage: {} <path/to/appinfo.vdf>", args[0]);
22        eprintln!();
23        eprintln!("Extracts AppId to game name mappings from Steam's appinfo.vdf file.");
24        eprintln!();
25        eprintln!("The appinfo.vdf file is typically located at:");
26        eprintln!("  Windows: C:\\Program Files (x86)\\Steam\\appcache\\appinfo.vdf");
27        eprintln!("  Linux:   ~/.steam/steam/appcache/appinfo.vdf");
28        eprintln!("  macOS:   ~/Library/Application Support/Steam/appcache/appinfo.vdf");
29        return ExitCode::FAILURE;
30    }
31
32    let path = &args[1];
33
34    // Read the file
35    let data = match fs::read(path) {
36        Ok(data) => data,
37        Err(e) => {
38            eprintln!("Error reading file '{}': {}", path, e);
39            return ExitCode::FAILURE;
40        }
41    };
42
43    // Parse the appinfo.vdf file
44    let vdf = match parse_appinfo(&data) {
45        Ok(vdf) => vdf.into_owned(),
46        Err(e) => {
47            eprintln!("Error parsing appinfo.vdf: {}", e);
48            return ExitCode::FAILURE;
49        }
50    };
51
52    // Get the root object containing all apps
53    let root = match vdf.as_obj() {
54        Some(obj) => obj,
55        None => {
56            eprintln!("Error: root is not an object");
57            return ExitCode::FAILURE;
58        }
59    };
60
61    // Iterate through all apps (keyed by AppID as string)
62    let mut apps = Vec::new();
63
64    for (app_id_str, app_value) in root.iter() {
65        // Skip non-numeric keys (metadata entries)
66        if app_id_str.parse::<u32>().is_err() {
67            continue;
68        }
69
70        let app_obj = match app_value.as_obj() {
71            Some(obj) => obj,
72            None => continue,
73        };
74
75        // Navigate the nested structure: appinfo -> common -> name
76        let name = app_obj
77            .get("appinfo")
78            .and_then(|v| v.as_obj())
79            .and_then(|appinfo| appinfo.get("common"))
80            .and_then(|common| common.as_obj())
81            .and_then(|common| common.get("name"))
82            .and_then(|v| v.as_str());
83
84        if let (Some(name), Ok(app_id)) = (name, app_id_str.parse::<u32>()) {
85            apps.push((app_id, name.to_string()));
86        }
87    }
88
89    // Sort by AppID
90    apps.sort_by_key(|(id, _)| *id);
91
92    // Print the results
93    println!("AppId\tName");
94    println!("------\t{}", "-".repeat(80));
95    for (app_id, name) in &apps {
96        println!("{}\t{}", app_id, name);
97    }
98
99    println!();
100    println!("Total games: {}", apps.len());
101
102    ExitCode::SUCCESS
103}
Source

pub fn iter(&self) -> impl Iterator<Item = (&Cow<'text, str>, &Value<'text>)>

Returns an iterator over the key-value pairs.

Examples found in repository?
examples/verify_compactified.rs (line 32)
6fn main() {
7    let args: Vec<String> = env::args().collect();
8
9    if args.len() < 2 {
10        eprintln!("Usage: {} <vdf_file>", args[0]);
11        std::process::exit(1);
12    }
13
14    let path = &args[1];
15
16    // Read the file
17    let data = match std::fs::read(path) {
18        Ok(d) => d,
19        Err(e) => {
20            eprintln!("Error reading file: {}", e);
21            std::process::exit(1);
22        }
23    };
24
25    // Parse it
26    match parse_appinfo(&data) {
27        Ok(vdf) => {
28            println!("Successfully parsed {}", path);
29            println!("Root key: {}", vdf.key());
30            if let Some(obj) = vdf.as_obj() {
31                println!("Number of apps: {}", obj.len());
32                for (key, _) in obj.iter().take(5) {
33                    println!("  - app_id: {}", key);
34                }
35                if obj.len() > 5 {
36                    println!("  ... and {} more", obj.len() - 5);
37                }
38            }
39        }
40        Err(e) => {
41            eprintln!("Error parsing file: {:?}", e);
42            std::process::exit(1);
43        }
44    }
45}
More examples
Hide additional examples
examples/dump_vdf.rs (line 19)
5fn dump_value(value: &steam_vdf_parser::Value, indent: usize) -> String {
6    let indent_str = "  ".repeat(indent);
7    match value {
8        steam_vdf_parser::Value::Str(s) => format!("{}\"{}\"", indent_str, s),
9        steam_vdf_parser::Value::I32(n) => format!("{}{}", indent_str, n),
10        steam_vdf_parser::Value::U64(n) => format!("{}{}", indent_str, n),
11        steam_vdf_parser::Value::Float(n) => format!("{}{}", indent_str, n),
12        steam_vdf_parser::Value::Pointer(n) => format!("{}(pointer: {})", indent_str, n),
13        steam_vdf_parser::Value::Color(c) => format!(
14            "{}(color: #{:02x}{:02x}{:02x}{:02x})",
15            indent_str, c[0], c[1], c[2], c[3]
16        ),
17        steam_vdf_parser::Value::Obj(obj) => {
18            let mut out = format!("{}{{\n", indent_str);
19            for (k, v) in obj.iter() {
20                out.push_str(&format!("{}\"\"{}\"\": ", indent_str, k));
21                match v {
22                    steam_vdf_parser::Value::Obj(_) => {
23                        out.push_str(&dump_value(v, indent + 1));
24                    }
25                    steam_vdf_parser::Value::Str(s) => out.push_str(&format!("\"{}\"\n", s)),
26                    steam_vdf_parser::Value::I32(n) => out.push_str(&format!("{}\n", n)),
27                    steam_vdf_parser::Value::U64(n) => out.push_str(&format!("{}\n", n)),
28                    steam_vdf_parser::Value::Float(n) => out.push_str(&format!("{}\n", n)),
29                    steam_vdf_parser::Value::Pointer(n) => {
30                        out.push_str(&format!("(pointer: {})\n", n))
31                    }
32                    steam_vdf_parser::Value::Color(c) => out.push_str(&format!(
33                        "(color: #{:02x}{:02x}{:02x}{:02x})\n",
34                        c[0], c[1], c[2], c[3]
35                    )),
36                }
37            }
38            out.push_str(&format!("{}}}\n", indent_str));
39            out
40        }
41    }
42}
examples/appid_to_name.rs (line 64)
17fn main() -> ExitCode {
18    let args: Vec<String> = env::args().collect();
19
20    if args.len() < 2 {
21        eprintln!("Usage: {} <path/to/appinfo.vdf>", args[0]);
22        eprintln!();
23        eprintln!("Extracts AppId to game name mappings from Steam's appinfo.vdf file.");
24        eprintln!();
25        eprintln!("The appinfo.vdf file is typically located at:");
26        eprintln!("  Windows: C:\\Program Files (x86)\\Steam\\appcache\\appinfo.vdf");
27        eprintln!("  Linux:   ~/.steam/steam/appcache/appinfo.vdf");
28        eprintln!("  macOS:   ~/Library/Application Support/Steam/appcache/appinfo.vdf");
29        return ExitCode::FAILURE;
30    }
31
32    let path = &args[1];
33
34    // Read the file
35    let data = match fs::read(path) {
36        Ok(data) => data,
37        Err(e) => {
38            eprintln!("Error reading file '{}': {}", path, e);
39            return ExitCode::FAILURE;
40        }
41    };
42
43    // Parse the appinfo.vdf file
44    let vdf = match parse_appinfo(&data) {
45        Ok(vdf) => vdf.into_owned(),
46        Err(e) => {
47            eprintln!("Error parsing appinfo.vdf: {}", e);
48            return ExitCode::FAILURE;
49        }
50    };
51
52    // Get the root object containing all apps
53    let root = match vdf.as_obj() {
54        Some(obj) => obj,
55        None => {
56            eprintln!("Error: root is not an object");
57            return ExitCode::FAILURE;
58        }
59    };
60
61    // Iterate through all apps (keyed by AppID as string)
62    let mut apps = Vec::new();
63
64    for (app_id_str, app_value) in root.iter() {
65        // Skip non-numeric keys (metadata entries)
66        if app_id_str.parse::<u32>().is_err() {
67            continue;
68        }
69
70        let app_obj = match app_value.as_obj() {
71            Some(obj) => obj,
72            None => continue,
73        };
74
75        // Navigate the nested structure: appinfo -> common -> name
76        let name = app_obj
77            .get("appinfo")
78            .and_then(|v| v.as_obj())
79            .and_then(|appinfo| appinfo.get("common"))
80            .and_then(|common| common.as_obj())
81            .and_then(|common| common.get("name"))
82            .and_then(|v| v.as_str());
83
84        if let (Some(name), Ok(app_id)) = (name, app_id_str.parse::<u32>()) {
85            apps.push((app_id, name.to_string()));
86        }
87    }
88
89    // Sort by AppID
90    apps.sort_by_key(|(id, _)| *id);
91
92    // Print the results
93    println!("AppId\tName");
94    println!("------\t{}", "-".repeat(80));
95    for (app_id, name) in &apps {
96        println!("{}\t{}", app_id, name);
97    }
98
99    println!();
100    println!("Total games: {}", apps.len());
101
102    ExitCode::SUCCESS
103}
Source

pub fn keys(&self) -> impl Iterator<Item = &str>

Returns an iterator over the keys.

Source

pub fn values(&self) -> impl Iterator<Item = &Value<'text>>

Returns an iterator over the values.

Source

pub fn contains_key(&self, key: &str) -> bool

Returns true if the object contains the given key.

Source

pub fn get_mut(&mut self, key: &str) -> Option<&mut Value<'text>>

Returns a mutable reference to the value corresponding to the key.

Source

pub fn insert( &mut self, key: impl Into<Cow<'text, str>>, value: Value<'text>, ) -> Option<Value<'text>>

Inserts a key-value pair into the object.

Returns the previous value if one existed for this key.

Source

pub fn remove(&mut self, key: &str) -> Option<Value<'text>>

Removes a key from the object, preserving insertion order.

This is O(n) as it shifts subsequent elements. Use swap_remove for O(1) removal when order doesn’t matter.

Returns the value if the key was present.

Source

pub fn swap_remove(&mut self, key: &str) -> Option<Value<'text>>

Removes a key from the object by swapping with the last element.

This is O(1) but does not preserve insertion order. Use remove if order preservation is needed.

Returns the value if the key was present.

Source§

impl Obj<'_>

Source

pub fn into_owned(self) -> Obj<'static>

Convert to an owned version (with ’static lifetime).

Trait Implementations§

Source§

impl<'text> Clone for Obj<'text>

Source§

fn clone(&self) -> Obj<'text>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'text> Debug for Obj<'text>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'text> Default for Obj<'text>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'text> Display for Obj<'text>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'text> From<Obj<'text>> for Value<'text>

Source§

fn from(obj: Obj<'text>) -> Self

Converts to this type from the input type.
Source§

impl<'text> Index<&str> for Obj<'text>

Source§

fn index(&self, key: &str) -> &Self::Output

Returns a reference to the value at the given key.

§Panics

Panics if the key doesn’t exist. Use get() for non-panicking access.

Source§

type Output = Value<'text>

The returned type after indexing.
Source§

impl<'a, 'text> IntoIterator for &'a Obj<'text>

Source§

type Item = (&'a Cow<'text, str>, &'a Value<'text>)

The type of the elements being iterated over.
Source§

type IntoIter = Iter<'a, Cow<'text, str>, Value<'text>>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl<'text> IntoIterator for Obj<'text>

Source§

type Item = (Cow<'text, str>, Value<'text>)

The type of the elements being iterated over.
Source§

type IntoIter = IntoIter<Cow<'text, str>, Value<'text>>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl<'text> PartialEq for Obj<'text>

Source§

fn eq(&self, other: &Obj<'text>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<'text> StructuralPartialEq for Obj<'text>

Auto Trait Implementations§

§

impl<'text> Freeze for Obj<'text>

§

impl<'text> RefUnwindSafe for Obj<'text>

§

impl<'text> Send for Obj<'text>

§

impl<'text> Sync for Obj<'text>

§

impl<'text> Unpin for Obj<'text>

§

impl<'text> UnwindSafe for Obj<'text>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.