use std::collections::HashMap;
use crate::error::Result;
#[derive(Debug, Clone)]
pub struct BuildConfig {
pub root_ckey: String,
pub encoding_ckey: String,
pub encoding_ekey: String,
pub encoding_size: Vec<u64>,
pub install_ckey: String,
pub install_ekey: String,
pub download_ckey: String,
pub download_ekey: String,
pub build_name: String,
pub build_uid: String,
pub build_product: String,
pub raw: HashMap<String, String>,
}
pub fn parse_build_config(content: &str) -> Result<BuildConfig> {
let mut raw: HashMap<String, String> = HashMap::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
if let Some((key, value)) = trimmed.split_once(" = ") {
raw.insert(key.to_string(), value.to_string());
}
}
let get = |key: &str| -> String { raw.get(key).cloned().unwrap_or_default() };
let split_pair = |key: &str| -> (String, String) {
let val = get(key);
let mut parts = val.splitn(2, ' ');
let first = parts.next().unwrap_or("").to_string();
let second = parts.next().unwrap_or("").to_string();
(first, second)
};
let parse_sizes = |key: &str| -> Vec<u64> {
let val = get(key);
if val.is_empty() {
return Vec::new();
}
val.split(' ')
.filter_map(|s| s.parse::<u64>().ok())
.collect()
};
let (encoding_ckey, encoding_ekey) = split_pair("encoding");
let (install_ckey, install_ekey) = split_pair("install");
let (download_ckey, download_ekey) = split_pair("download");
Ok(BuildConfig {
root_ckey: get("root"),
encoding_ckey,
encoding_ekey,
encoding_size: parse_sizes("encoding-size"),
install_ckey,
install_ekey,
download_ckey,
download_ekey,
build_name: get("build-name"),
build_uid: get("build-uid"),
build_product: get("build-product"),
raw,
})
}
pub fn config_path(hash: &str) -> String {
format!("config/{}/{}/{}", &hash[..2], &hash[2..4], hash)
}
#[cfg(test)]
mod tests {
use super::*;
const FIXTURE: &str = "# Build Configuration\nroot = 0ff1247849a5cd6049624d3a105811f8\ninstall = a33a459aa7585d626a3e4209858b4eed a9f3ece675323e1bbd2c17a765adc3c4\ninstall-size = 23286 22368\ndownload = 108fa2da5f5337d8eb4e35e0d3573925 697b5503715187be840f15ee5862adf4\ndownload-size = 72504201 61674341\nencoding = d2f601fe389b9a1133709e716899a633 d3e25753d9f33b6ab55c56532e2131cc\nencoding-size = 194221474 182793061\nbuild-name = WOW-66192patch12.0.1_Retail\nbuild-uid = wow\nbuild-product = WoW\n";
#[test]
fn parse_root_ckey() {
let config = parse_build_config(FIXTURE).unwrap();
assert_eq!(config.root_ckey, "0ff1247849a5cd6049624d3a105811f8");
}
#[test]
fn parse_encoding_pair() {
let config = parse_build_config(FIXTURE).unwrap();
assert_eq!(config.encoding_ckey, "d2f601fe389b9a1133709e716899a633");
assert_eq!(config.encoding_ekey, "d3e25753d9f33b6ab55c56532e2131cc");
}
#[test]
fn parse_encoding_sizes() {
let config = parse_build_config(FIXTURE).unwrap();
assert_eq!(config.encoding_size, vec![194221474, 182793061]);
}
#[test]
fn parse_build_name() {
let config = parse_build_config(FIXTURE).unwrap();
assert_eq!(config.build_name, "WOW-66192patch12.0.1_Retail");
}
#[test]
fn parse_build_uid() {
let config = parse_build_config(FIXTURE).unwrap();
assert_eq!(config.build_uid, "wow");
}
#[test]
fn parse_skips_comments() {
let data = "# comment\nroot = abc123\n";
let config = parse_build_config(data).unwrap();
assert_eq!(config.root_ckey, "abc123");
}
#[test]
fn parse_skips_empty_lines() {
let data = "\n\nroot = abc123\n\n";
let config = parse_build_config(data).unwrap();
assert_eq!(config.root_ckey, "abc123");
}
#[test]
fn config_path_from_hash() {
let path = config_path("13e1eb56839dfaf734d7fab21b0c8ea4");
assert_eq!(path, "config/13/e1/13e1eb56839dfaf734d7fab21b0c8ea4");
}
}