use std::collections::BTreeSet;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use anyhow::Context;
use anyhow::Result;
use crate::binary_format::AnalyzeData;
#[derive(Debug, Clone, Default)]
pub struct ModuleSize {
pub size: u64,
pub compressed_size: u64,
}
pub struct RouteData {
pub sizes: HashMap<String, ModuleSize>,
pub route_membership: HashMap<String, BTreeSet<String>>,
}
pub fn load_route_data(data_dir: &Path) -> Result<RouteData> {
let routes = read_routes(data_dir)?;
let mut sizes: HashMap<String, ModuleSize> = HashMap::new();
let mut route_membership: HashMap<String, BTreeSet<String>> = HashMap::new();
for route in &routes {
let analyze_path = route_to_analyze_path(data_dir, route);
if !analyze_path.exists() {
continue;
}
let bytes = fs::read(&analyze_path)
.with_context(|| format!("reading {}", analyze_path.display()))?;
let data = AnalyzeData::from_bytes(&bytes)?;
process_route(&data, route, &mut sizes, &mut route_membership);
}
Ok(RouteData {
sizes,
route_membership,
})
}
fn read_routes(data_dir: &Path) -> Result<Vec<String>> {
let routes_path = data_dir.join("routes.json");
if !routes_path.exists() {
return Ok(Vec::new());
}
let contents = fs::read_to_string(&routes_path).context("failed to read routes.json")?;
let routes: Vec<String> =
serde_json::from_str(&contents).context("failed to parse routes.json")?;
Ok(routes)
}
fn route_to_analyze_path(data_dir: &Path, route: &str) -> std::path::PathBuf {
if route == "/" {
data_dir.join("analyze.data")
} else {
let stripped = route.strip_prefix('/').unwrap_or(route);
data_dir.join(stripped).join("analyze.data")
}
}
fn process_route(
data: &AnalyzeData,
route: &str,
sizes: &mut HashMap<String, ModuleSize>,
route_membership: &mut HashMap<String, BTreeSet<String>>,
) {
let mut route_sizes: HashMap<String, ModuleSize> = HashMap::new();
for chunk_part in &data.header.chunk_parts {
let full_path = data.full_source_path(chunk_part.source_index as usize);
if full_path.is_empty() {
continue;
}
let entry = route_sizes.entry(full_path.clone()).or_default();
entry.size += chunk_part.size as u64;
entry.compressed_size += chunk_part.compressed_size as u64;
route_membership
.entry(full_path)
.or_default()
.insert(route.to_string());
}
for (path, route_size) in route_sizes {
let entry = sizes.entry(path).or_default();
entry.size = entry.size.max(route_size.size);
entry.compressed_size = entry.compressed_size.max(route_size.compressed_size);
}
}