pub mod graph;
pub mod metadata;
pub mod order;
pub mod version;
pub use graph::{build_dependency_graph, get_graph_stats, get_publish_order};
pub use order::{build_batches, build_publication_order, get_skipped_crates};
pub use version::analyze_versions;
use crate::cli::{OutputFormat, PlanOpts};
use crate::error::{CommandExitCode, Result};
use metadata::parse_cargo_metadata;
use serde_json::json;
pub struct PlanService {
workspace_path: String,
opts: PlanOpts,
}
impl PlanService {
pub const fn new(workspace_path: String, opts: PlanOpts) -> Self {
Self {
workspace_path,
opts,
}
}
pub async fn execute(&self, _format: OutputFormat) -> Result<CommandExitCode> {
let start_time = std::time::Instant::now();
let (metadata, packages) = parse_cargo_metadata(&self.workspace_path)?;
let dep_graph = build_dependency_graph(&packages);
let has_cycles = dep_graph.has_cycles();
let publish_order = get_publish_order(&dep_graph, has_cycles);
let (bump_type, current, new_version) = analyze_versions(&self.workspace_path).await?;
let publication_order = build_publication_order(&publish_order, &new_version, &dep_graph);
let crates_to_publish = publication_order.len();
let total_estimated_time = crates_to_publish * 45;
let batches = build_batches(&publish_order);
let skipped_crates = get_skipped_crates(self.opts.include_published, &new_version);
let (nodes, edges, cycles) = get_graph_stats(&dep_graph, has_cycles);
let workspace_name = metadata
.workspace_root
.file_name()
.map_or_else(|| "workspace".to_string(), std::string::ToString::to_string);
let response = json!({
"success": !has_cycles,
"command": "plan",
"workspace": workspace_name,
"result": {
"version_analysis": {
"current": current.to_string(),
"new": new_version.to_string(),
"bump": serde_json::to_value(bump_type).unwrap(),
},
"dependency_graph": {
"nodes": nodes,
"edges": edges,
"cycles": cycles,
},
"publication_order": publication_order,
"crates_to_publish": crates_to_publish,
"crates_to_skip": skipped_crates.len(),
"skipped_crates": skipped_crates,
"total_estimated_time_sec": total_estimated_time,
"batches": batches,
},
"metrics": {
"execution_time_ms": start_time.elapsed().as_millis(),
"git_operations": 1,
"api_calls": 0,
}
});
println!("{}", serde_json::to_string_pretty(&response).unwrap());
if has_cycles {
Ok(CommandExitCode::GeneralError)
} else {
Ok(CommandExitCode::Success)
}
}
}