cargo-governor 2.0.3

Machine-First, LLM-Ready, CI/CD-Native release automation tool for Rust crates
Documentation
//! Simulate service - business logic for release simulation

pub mod commits;
pub mod impact;
pub mod recommendations;
pub mod version;

pub use commits::analyze_commits;
pub use impact::simulate_downstream_impact;
pub use recommendations::generate_recommendations;
pub use version::{create_version_diff, determine_target_version, get_current_version};

use crate::cli::{OutputFormat, SimulateOpts};
use crate::error::{CommandExitCode, Result};
use governor_core::domain::version::SemanticVersion;
use governor_core::traits::source_control::SourceControl;
use governor_git::GitAdapter;
use serde_json::json;
use std::path::PathBuf;

/// Service for simulating releases
pub struct SimulateService {
    workspace_path: String,
    opts: SimulateOpts,
}

impl SimulateService {
    pub const fn new(workspace_path: String, opts: SimulateOpts) -> Self {
        Self {
            workspace_path,
            opts,
        }
    }

    pub async fn execute(&self, _format: OutputFormat) -> Result<CommandExitCode> {
        let start_time = std::time::Instant::now();

        let current_version_str = get_current_version(&self.workspace_path)?;
        let current = SemanticVersion::parse(&current_version_str).map_err(|e| {
            crate::error::Error::Version(format!("Failed to parse current version: {e}"))
        })?;

        let target_version =
            determine_target_version(&self.workspace_path, self.opts.version.as_deref(), &current)
                .await?;

        let config = governor_git::GitAdapterConfig {
            repository_path: Some(PathBuf::from(&self.workspace_path)),
            ..Default::default()
        };

        let git = GitAdapter::open(config).map_err(|e| {
            crate::error::Error::Config(format!("Failed to open git repository: {e}"))
        })?;

        let commits = git
            .get_commits_since(None)
            .await
            .map_err(|e| crate::error::Error::Git(format!("Failed to get commits: {e}")))?;

        let (breaking, features, fixes, other) = analyze_commits(&commits);

        let downstream_count = self.opts.downstream_crates.unwrap_or(0);
        let downstream_impact = if downstream_count > 0 {
            simulate_downstream_impact(downstream_count, &target_version)
        } else {
            Vec::new()
        };

        let version_diff = create_version_diff(&current, &target_version);

        let response = json!({
            "success": true,
            "command": "simulate",
            "workspace": self.workspace_path,
            "result": {
                "version_diff": version_diff,
                "changes": {
                    "breaking_changes": breaking.len(),
                    "features": features.len(),
                    "fixes": fixes.len(),
                    "other": other,
                },
                "breaking_details": breaking,
                "features_details": features,
                "fixes_details": fixes,
                "downstream_impact": downstream_impact,
                "recommendations": generate_recommendations(breaking.len(), features.len(), fixes.len()),
            },
            "metrics": {
                "execution_time_ms": start_time.elapsed().as_millis(),
                "git_operations": 1,
                "api_calls": 0,
            }
        });

        println!("{}", serde_json::to_string_pretty(&response).unwrap());
        Ok(CommandExitCode::Success)
    }
}