use std::collections::BTreeMap;
use humansize::{DECIMAL, format_size};
use serde::Serialize;
use crate::project::{Project, ProjectType};
#[derive(Debug, Serialize)]
pub struct JsonOutput {
pub mode: String,
pub projects: Vec<JsonProjectEntry>,
pub summary: JsonSummary,
#[serde(skip_serializing_if = "Option::is_none")]
pub cleanup: Option<JsonCleanupResult>,
}
#[derive(Debug, Serialize)]
pub struct JsonProjectEntry {
pub name: Option<String>,
#[serde(rename = "type")]
pub project_type: ProjectType,
pub root_path: String,
pub build_artifacts_paths: Vec<String>,
pub build_artifacts_size: u64,
pub build_artifacts_size_formatted: String,
}
#[derive(Debug, Serialize)]
pub struct JsonSummary {
pub total_projects: usize,
pub total_size: u64,
pub total_size_formatted: String,
pub by_type: BTreeMap<String, JsonTypeSummary>,
}
#[derive(Debug, Serialize)]
pub struct JsonTypeSummary {
pub count: usize,
pub size: u64,
pub size_formatted: String,
}
#[derive(Debug, Serialize)]
pub struct JsonCleanupResult {
pub success_count: usize,
pub failure_count: usize,
pub total_freed: u64,
pub total_freed_formatted: String,
pub errors: Vec<String>,
}
impl JsonOutput {
#[must_use]
pub fn from_projects_dry_run(projects: &[Project]) -> Self {
Self {
mode: "dry_run".to_string(),
projects: projects
.iter()
.map(JsonProjectEntry::from_project)
.collect(),
summary: JsonSummary::from_projects(projects),
cleanup: None,
}
}
#[must_use]
pub fn from_projects_cleanup(
projects: &[Project],
clean_result: &crate::cleaner::CleanResult,
) -> Self {
Self {
mode: "cleanup".to_string(),
projects: projects
.iter()
.map(JsonProjectEntry::from_project)
.collect(),
summary: JsonSummary::from_projects(projects),
cleanup: Some(JsonCleanupResult::from_clean_result(clean_result)),
}
}
}
impl JsonProjectEntry {
#[must_use]
pub fn from_project(project: &Project) -> Self {
let total = project.total_size();
Self {
name: project.name.clone(),
project_type: project.kind.clone(),
root_path: project.root_path.display().to_string(),
build_artifacts_paths: project
.build_arts
.iter()
.map(|a| a.path.display().to_string())
.collect(),
build_artifacts_size: total,
build_artifacts_size_formatted: format_size(total, DECIMAL),
}
}
}
impl JsonSummary {
#[must_use]
pub fn from_projects(projects: &[Project]) -> Self {
let mut by_type: BTreeMap<String, (usize, u64)> = BTreeMap::new();
for project in projects {
let key = match project.kind {
ProjectType::Rust => "rust",
ProjectType::Node => "node",
ProjectType::Python => "python",
ProjectType::Go => "go",
ProjectType::Java => "java",
ProjectType::Cpp => "cpp",
ProjectType::Swift => "swift",
ProjectType::DotNet => "dotnet",
ProjectType::Ruby => "ruby",
ProjectType::Elixir => "elixir",
ProjectType::Deno => "deno",
ProjectType::Php => "php",
ProjectType::Haskell => "haskell",
ProjectType::Dart => "dart",
ProjectType::Zig => "zig",
ProjectType::Scala => "scala",
};
let entry = by_type.entry(key.to_string()).or_insert((0, 0));
entry.0 += 1;
entry.1 += project.total_size();
}
let total_size: u64 = projects.iter().map(Project::total_size).sum();
Self {
total_projects: projects.len(),
total_size,
total_size_formatted: format_size(total_size, DECIMAL),
by_type: by_type
.into_iter()
.map(|(k, (count, size))| {
(
k,
JsonTypeSummary {
count,
size,
size_formatted: format_size(size, DECIMAL),
},
)
})
.collect(),
}
}
}
impl JsonCleanupResult {
#[must_use]
pub fn from_clean_result(result: &crate::cleaner::CleanResult) -> Self {
Self {
success_count: result.success_count,
failure_count: result.errors.len(),
total_freed: result.total_freed,
total_freed_formatted: format_size(result.total_freed, DECIMAL),
errors: result.errors.clone(),
}
}
}