govctl 0.9.0

Project governance CLI for RFC, ADR, and Work Item management
use super::paths::{require_replacement_rfc_toml_path, require_rfc_toml_path};
use crate::config::Config;
use crate::diagnostic::{Diagnostic, DiagnosticCode, DiagnosticResult, Diagnostics};
use crate::model::RfcStatus;
use crate::ui;
use crate::validate::is_valid_status_transition;
use crate::write::{WriteOp, read_rfc, today, write_rfc};

pub(super) fn supersede_rfc(
    config: &Config,
    rfc_id: &str,
    by: &str,
    op: WriteOp,
) -> DiagnosticResult<Diagnostics> {
    if rfc_id == by {
        return Err(Diagnostic::new(
            DiagnosticCode::E0802ConflictingArgs,
            "RFC cannot supersede itself",
            rfc_id,
        ));
    }

    let rfc_path = require_rfc_toml_path(config, rfc_id)?;
    let replacement_path = require_replacement_rfc_toml_path(config, by)?;
    let mut source = read_rfc(config, &rfc_path)?;
    let mut replacement = read_rfc(config, &replacement_path)?;

    validate_supersede_transition(&source, &replacement, rfc_id, by)?;

    let today = today();
    source.status = RfcStatus::Deprecated;
    source.updated = Some(today.clone());
    replacement.supersedes = Some(rfc_id.to_string());
    replacement.updated = Some(today);

    write_rfc(
        &rfc_path,
        &source,
        op,
        Some(&config.display_path(&rfc_path)),
    )?;
    write_rfc(
        &replacement_path,
        &replacement,
        op,
        Some(&config.display_path(&replacement_path)),
    )?;

    if !op.is_preview() {
        ui::superseded("RFC", rfc_id, by);
    }
    Ok(vec![])
}

fn validate_supersede_transition(
    source: &crate::model::RfcSpec,
    replacement: &crate::model::RfcSpec,
    rfc_id: &str,
    by: &str,
) -> DiagnosticResult<()> {
    if !is_valid_status_transition(source.status, RfcStatus::Deprecated) {
        return Err(Diagnostic::new(
            DiagnosticCode::E0104RfcInvalidTransition,
            format!(
                "Invalid RFC transition: {} -> deprecated",
                source.status.as_ref()
            ),
            rfc_id,
        ));
    }
    if replacement.status == RfcStatus::Deprecated {
        return Err(Diagnostic::new(
            DiagnosticCode::E0104RfcInvalidTransition,
            format!("Replacement RFC is deprecated: {by}"),
            by,
        ));
    }
    if replacement
        .supersedes
        .as_deref()
        .is_some_and(|old| old != rfc_id)
    {
        return Err(Diagnostic::new(
            DiagnosticCode::E0104RfcInvalidTransition,
            format!(
                "Replacement RFC already supersedes {}",
                replacement.supersedes.as_deref().unwrap_or_default()
            ),
            by,
        ));
    }

    Ok(())
}