use std::path::Path;
use dbmd_core::stats::{self, Stats};
use dbmd_core::Store;
use crate::cli::StatsArgs;
use crate::context::Context;
use crate::error::CliResult;
pub fn run(ctx: &Context, args: &StatsArgs) -> CliResult {
let store = Store::open(Path::new(&args.dir)).map_err(dbmd_core::Error::from)?;
let s = stats::compute(&store)?;
if ctx.json {
emit_json(&s);
} else {
emit_text(&s);
}
Ok(())
}
fn emit_text(s: &Stats) {
println!("files: {}", s.total_files);
for layer in dbmd_core::store::Layer::all() {
let n = s.files_per_layer.get(&layer).copied().unwrap_or(0);
println!(" {}: {}", layer.dir_name(), n);
}
println!("size: {} bytes", s.total_size_bytes);
println!("orphans: {}", s.orphan_count);
println!("broken links: {}", s.broken_link_count);
if !s.top_types.is_empty() {
println!("top types:");
for (type_, count) in &s.top_types {
println!(" {type_}: {count}");
}
}
if !s.recognized_types_present.is_empty() {
println!(
"recognized types: {}",
s.recognized_types_present.join(", ")
);
}
if !s.custom_types_present.is_empty() {
println!("custom types: {}", s.custom_types_present.join(", "));
}
}
fn emit_json(s: &Stats) {
let files_per_layer: serde_json::Map<String, serde_json::Value> = s
.files_per_layer
.iter()
.map(|(layer, n)| (layer.dir_name().to_string(), serde_json::json!(n)))
.collect();
let type_distribution: serde_json::Map<String, serde_json::Value> = s
.type_distribution
.iter()
.map(|(t, n)| (t.clone(), serde_json::json!(n)))
.collect();
let top_types: Vec<serde_json::Value> = s
.top_types
.iter()
.map(|(t, n)| serde_json::json!([t, n]))
.collect();
let out = serde_json::json!({
"total_files": s.total_files,
"files_per_layer": files_per_layer,
"total_size_bytes": s.total_size_bytes,
"type_distribution": type_distribution,
"orphan_count": s.orphan_count,
"broken_link_count": s.broken_link_count,
"top_types": top_types,
"recognized_types_present": s.recognized_types_present,
"custom_types_present": s.custom_types_present,
});
println!("{}", serde_json::to_string(&out).expect("serialize stats"));
}