pub struct Vdf<'text> { /* private fields */ }Expand description
Top-level VDF document
A VDF document is essentially a single key-value pair at the root level.
Implementations§
Source§impl<'text> Vdf<'text>
impl<'text> Vdf<'text>
Sourcepub fn new(key: impl Into<Cow<'text, str>>, value: Value<'text>) -> Self
pub fn new(key: impl Into<Cow<'text, str>>, value: Value<'text>) -> Self
Creates a new VDF document.
Sourcepub fn key(&self) -> &str
pub fn key(&self) -> &str
Returns the root key.
Examples found in repository?
examples/verify_compactified.rs (line 29)
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
examples/test_parse_real.rs (line 14)
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}examples/dump_vdf.rs (line 99)
44fn main() {
45 let args: Vec<String> = env::args().collect();
46 if args.len() < 2 {
47 eprintln!("Usage: {} <vdf_file> [--text]", args[0]);
48 eprintln!(
49 " --text: force text format parsing (default: auto-detect based on file extension)"
50 );
51 std::process::exit(1);
52 }
53
54 let path = Path::new(&args[1]);
55 let force_text = args.len() > 2 && args[2] == "--text";
56
57 let result = if force_text || path.extension().is_some_and(|e| e == "vdf") {
58 // Try text first for .vdf files
59 let content = std::fs::read_to_string(path);
60 if let Ok(content) = content {
61 parse_text(&content).map(|v| v.into_owned())
62 } else {
63 // Fall back to binary
64 let data = std::fs::read(path).expect("Failed to read file");
65 if path
66 .file_name()
67 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
68 {
69 binary::parse_packageinfo(&data).map(|v| v.into_owned())
70 } else if path
71 .file_name()
72 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
73 {
74 binary::parse_appinfo(&data).map(|v| v.into_owned())
75 } else {
76 parse_binary(&data).map(|v| v.into_owned())
77 }
78 }
79 } else {
80 // Binary parsing
81 let data = std::fs::read(path).expect("Failed to read file");
82 if path
83 .file_name()
84 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
85 {
86 binary::parse_packageinfo(&data).map(|v| v.into_owned())
87 } else if path
88 .file_name()
89 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
90 {
91 binary::parse_appinfo(&data).map(|v| v.into_owned())
92 } else {
93 parse_binary(&data).map(|v| v.into_owned())
94 }
95 };
96
97 match result {
98 Ok(vdf) => {
99 println!("\"{}\" {}", vdf.key(), dump_value(vdf.value(), 0));
100 }
101 Err(e) => {
102 eprintln!("Error parsing VDF: {:?}", e);
103 std::process::exit(1);
104 }
105 }
106}Sourcepub fn value(&self) -> &Value<'text>
pub fn value(&self) -> &Value<'text>
Returns a reference to the root value.
Examples found in repository?
examples/dump_vdf.rs (line 99)
44fn main() {
45 let args: Vec<String> = env::args().collect();
46 if args.len() < 2 {
47 eprintln!("Usage: {} <vdf_file> [--text]", args[0]);
48 eprintln!(
49 " --text: force text format parsing (default: auto-detect based on file extension)"
50 );
51 std::process::exit(1);
52 }
53
54 let path = Path::new(&args[1]);
55 let force_text = args.len() > 2 && args[2] == "--text";
56
57 let result = if force_text || path.extension().is_some_and(|e| e == "vdf") {
58 // Try text first for .vdf files
59 let content = std::fs::read_to_string(path);
60 if let Ok(content) = content {
61 parse_text(&content).map(|v| v.into_owned())
62 } else {
63 // Fall back to binary
64 let data = std::fs::read(path).expect("Failed to read file");
65 if path
66 .file_name()
67 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
68 {
69 binary::parse_packageinfo(&data).map(|v| v.into_owned())
70 } else if path
71 .file_name()
72 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
73 {
74 binary::parse_appinfo(&data).map(|v| v.into_owned())
75 } else {
76 parse_binary(&data).map(|v| v.into_owned())
77 }
78 }
79 } else {
80 // Binary parsing
81 let data = std::fs::read(path).expect("Failed to read file");
82 if path
83 .file_name()
84 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
85 {
86 binary::parse_packageinfo(&data).map(|v| v.into_owned())
87 } else if path
88 .file_name()
89 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
90 {
91 binary::parse_appinfo(&data).map(|v| v.into_owned())
92 } else {
93 parse_binary(&data).map(|v| v.into_owned())
94 }
95 };
96
97 match result {
98 Ok(vdf) => {
99 println!("\"{}\" {}", vdf.key(), dump_value(vdf.value(), 0));
100 }
101 Err(e) => {
102 eprintln!("Error parsing VDF: {:?}", e);
103 std::process::exit(1);
104 }
105 }
106}Sourcepub fn value_mut(&mut self) -> &mut Value<'text>
pub fn value_mut(&mut self) -> &mut Value<'text>
Returns a mutable reference to the root value.
Sourcepub fn into_parts(self) -> (Cow<'text, str>, Value<'text>)
pub fn into_parts(self) -> (Cow<'text, str>, Value<'text>)
Consumes the Vdf and returns its parts.
Sourcepub fn as_obj(&self) -> Option<&Obj<'text>>
pub fn as_obj(&self) -> Option<&Obj<'text>>
Returns a reference to the root object if it is one.
Examples found in repository?
examples/verify_compactified.rs (line 30)
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
examples/test_parse_real.rs (line 15)
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}examples/appid_to_name.rs (line 53)
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}Sourcepub fn get(&self, key: &str) -> Option<&Value<'text>>
pub fn get(&self, key: &str) -> Option<&Value<'text>>
Returns a reference to a nested value by key.
Source§impl Vdf<'_>
impl Vdf<'_>
Sourcepub fn into_owned(self) -> Vdf<'static>
pub fn into_owned(self) -> Vdf<'static>
Convert to an owned version (with ’static lifetime).
This creates a new Vdf<'static> with all strings owned, allowing the
data to outlive the original input.
Examples found in repository?
examples/print.rs (line 29)
12fn main() {
13 let args: Vec<String> = env::args().collect();
14 if args.len() < 2 {
15 eprintln!("Usage: {} <vdf_file>", args[0]);
16 eprintln!();
17 eprintln!("Pretty-prints a VDF file to stdout in valid VDF text format.");
18 eprintln!("Supports both text (.vdf) and binary (appinfo.vdf, packageinfo.vdf, shortcuts.vdf) formats.");
19 std::process::exit(1);
20 }
21
22 let path = Path::new(&args[1]);
23 let data = std::fs::read(path).expect("Failed to read file");
24
25 // Try to detect format and parse accordingly
26 let result = if let Ok(text) = std::str::from_utf8(&data) {
27 // Looks like text, try text parser first
28 parse_text(text)
29 .map(|v| v.into_owned())
30 .or_else(|_| parse_binary(&data).map(|v| v.into_owned()))
31 } else {
32 // Binary data - detect format from filename
33 let filename = path
34 .file_name()
35 .and_then(|n| n.to_str())
36 .unwrap_or_default();
37
38 if filename.contains("packageinfo") {
39 binary::parse_packageinfo(&data).map(|v| v.into_owned())
40 } else if filename.contains("appinfo") {
41 binary::parse_appinfo(&data).map(|v| v.into_owned())
42 } else {
43 parse_binary(&data).map(|v| v.into_owned())
44 }
45 };
46
47 match result {
48 Ok(vdf) => {
49 // Use the pretty-print Display implementation
50 println!("{:#}", vdf);
51 }
52 Err(e) => {
53 eprintln!("Error parsing VDF: {:?}", e);
54 std::process::exit(1);
55 }
56 }
57}More examples
examples/dump_vdf.rs (line 61)
44fn main() {
45 let args: Vec<String> = env::args().collect();
46 if args.len() < 2 {
47 eprintln!("Usage: {} <vdf_file> [--text]", args[0]);
48 eprintln!(
49 " --text: force text format parsing (default: auto-detect based on file extension)"
50 );
51 std::process::exit(1);
52 }
53
54 let path = Path::new(&args[1]);
55 let force_text = args.len() > 2 && args[2] == "--text";
56
57 let result = if force_text || path.extension().is_some_and(|e| e == "vdf") {
58 // Try text first for .vdf files
59 let content = std::fs::read_to_string(path);
60 if let Ok(content) = content {
61 parse_text(&content).map(|v| v.into_owned())
62 } else {
63 // Fall back to binary
64 let data = std::fs::read(path).expect("Failed to read file");
65 if path
66 .file_name()
67 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
68 {
69 binary::parse_packageinfo(&data).map(|v| v.into_owned())
70 } else if path
71 .file_name()
72 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
73 {
74 binary::parse_appinfo(&data).map(|v| v.into_owned())
75 } else {
76 parse_binary(&data).map(|v| v.into_owned())
77 }
78 }
79 } else {
80 // Binary parsing
81 let data = std::fs::read(path).expect("Failed to read file");
82 if path
83 .file_name()
84 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("packageinfo")))
85 {
86 binary::parse_packageinfo(&data).map(|v| v.into_owned())
87 } else if path
88 .file_name()
89 .is_some_and(|n| n.to_str().is_some_and(|s| s.contains("appinfo")))
90 {
91 binary::parse_appinfo(&data).map(|v| v.into_owned())
92 } else {
93 parse_binary(&data).map(|v| v.into_owned())
94 }
95 };
96
97 match result {
98 Ok(vdf) => {
99 println!("\"{}\" {}", vdf.key(), dump_value(vdf.value(), 0));
100 }
101 Err(e) => {
102 eprintln!("Error parsing VDF: {:?}", e);
103 std::process::exit(1);
104 }
105 }
106}examples/appid_to_name.rs (line 45)
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}Trait Implementations§
impl<'text> StructuralPartialEq for Vdf<'text>
Auto Trait Implementations§
impl<'text> Freeze for Vdf<'text>
impl<'text> RefUnwindSafe for Vdf<'text>
impl<'text> Send for Vdf<'text>
impl<'text> Sync for Vdf<'text>
impl<'text> Unpin for Vdf<'text>
impl<'text> UnwindSafe for Vdf<'text>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more