wows_data_mgr/
manifest.rs1use std::collections::BTreeMap;
2use std::path::Path;
3
4use rootcause::prelude::*;
5use serde::Deserialize;
6use serde::Serialize;
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct GameVersionManifest {
10 #[serde(default)]
11 pub versions: BTreeMap<String, GameVersionEntry>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct GameVersionEntry {
16 pub version: String,
17 pub depot_id: u32,
18 pub manifest_id: String,
19}
20
21impl GameVersionManifest {
22 pub fn latest_build(&self) -> Option<u32> {
24 self.versions.keys().filter_map(|k| k.parse::<u32>().ok()).max()
25 }
26
27 pub fn find_by_version(&self, query: &str) -> Option<u32> {
30 let mut matched: Vec<u32> = self
31 .versions
32 .iter()
33 .filter(|(_, entry)| version_matches(&entry.version, query))
34 .filter_map(|(k, _)| k.parse::<u32>().ok())
35 .collect();
36 matched.sort();
37 matched.last().copied()
38 }
39
40 pub fn get(&self, build: u32) -> Option<&GameVersionEntry> {
42 self.versions.get(&build.to_string())
43 }
44}
45
46pub fn version_matches(full: &str, query: &str) -> bool {
49 let full_parts: Vec<&str> = full.split('.').collect();
50 let query_parts: Vec<&str> = query.split('.').collect();
51
52 if query_parts.len() > full_parts.len() {
53 return false;
54 }
55
56 full_parts.iter().zip(query_parts.iter()).all(|(f, q)| f == q)
57}
58
59pub fn load_manifest(path: &Path) -> Result<GameVersionManifest, Report> {
60 if !path.exists() {
61 return Ok(GameVersionManifest { versions: BTreeMap::new() });
62 }
63 let content = std::fs::read_to_string(path).attach_with(|| format!("Failed to read {}", path.display()))?;
64 let manifest: GameVersionManifest =
65 toml::from_str(&content).map_err(|e| rootcause::report!("Failed to parse {}: {e}", path.display()))?;
66 Ok(manifest)
67}
68
69#[cfg(test)]
70mod test {
71 use super::*;
72
73 #[test]
74 fn version_matches_exact() {
75 assert!(version_matches("15.1.0", "15.1.0"));
76 }
77
78 #[test]
79 fn version_matches_shorthand_two() {
80 assert!(version_matches("15.1.0", "15.1"));
81 }
82
83 #[test]
84 fn version_matches_shorthand_one() {
85 assert!(version_matches("15.1.0", "15"));
86 }
87
88 #[test]
89 fn version_no_match() {
90 assert!(!version_matches("15.1.0", "14.1"));
91 }
92
93 #[test]
94 fn version_query_longer() {
95 assert!(!version_matches("15.1", "15.1.0"));
96 }
97
98 #[test]
99 fn find_by_version_picks_highest() {
100 let mut versions = BTreeMap::new();
101 versions.insert(
102 "11791718".to_string(),
103 GameVersionEntry { version: "15.0.0".to_string(), depot_id: 552991, manifest_id: "aaa".to_string() },
104 );
105 versions.insert(
106 "11965230".to_string(),
107 GameVersionEntry { version: "15.1.0".to_string(), depot_id: 552991, manifest_id: "bbb".to_string() },
108 );
109 let manifest = GameVersionManifest { versions };
110
111 assert_eq!(manifest.find_by_version("15"), Some(11965230));
112 assert_eq!(manifest.find_by_version("15.1"), Some(11965230));
113 assert_eq!(manifest.find_by_version("15.0"), Some(11791718));
114 assert_eq!(manifest.find_by_version("14"), None);
115 }
116}