shiplog 0.9.0

CLI evidence compiler for review-cycle packets with receipts, coverage, gaps, and safe share profiles.
Documentation
use anyhow::Context;
use std::path::PathBuf;

use crate::*;

#[expect(clippy::too_many_arguments, reason = "policy:clippy-0001")]
pub(super) fn handle(
    inputs: Vec<PathBuf>,
    out: PathBuf,
    conflict: MergeConflict,
    user: Option<String>,
    window_label: Option<String>,
    zip: bool,
    redact_key: Option<String>,
    bundle_profile: BundleProfile,
    regen: bool,
) -> Result<()> {
    let redaction_key = RedactionKey::resolve(redact_key, &bundle_profile)?;
    let clusterer: Box<dyn shiplog::ports::WorkstreamClusterer> = Box::new(RepoClusterer);
    let (engine, redactor) = create_engine(redaction_key.engine_key(), clusterer, &bundle_profile);
    let engine = engine.with_profile_rendering(redaction_key.render_profiles());

    let mut ingest_outputs = Vec::with_capacity(inputs.len());
    for input in &inputs {
        ingest_outputs.push(
            load_run_ingest(input)
                .with_context(|| format!("load merge input {}", input.display()))?,
        );
    }

    let merged = engine
        .merge(ingest_outputs, conflict.into())
        .context("merge input runs")?;
    let merge_user = user.unwrap_or_else(|| merged.coverage.user.clone());
    let merge_window_label = window_label.unwrap_or_else(|| {
        format!(
            "{}..{}",
            merged.coverage.window.since, merged.coverage.window.until
        )
    });
    let run_id = merged.coverage.run_id.to_string();
    let run_dir = out.join(&run_id);

    if regen {
        let suggested = shiplog::workstreams::WorkstreamManager::suggested_path(&run_dir);
        if suggested.exists() {
            std::fs::remove_file(&suggested)
                .with_context(|| format!("remove {:?} for --regen", suggested))?;
        }
    }

    let cache_path = DeterministicRedactor::cache_path(&run_dir);
    let _ = redactor.load_cache(&cache_path);

    let (outputs, ws_source) = engine
        .run(
            merged,
            &merge_user,
            &merge_window_label,
            &run_dir,
            zip,
            &bundle_profile,
        )
        .context("run merged engine pipeline")?;

    redactor
        .save_cache(&cache_path)
        .with_context(|| format!("save redaction cache to {cache_path:?}"))?;

    println!("Merged and wrote:");
    println!("- inputs: {}", inputs.len());
    println!("- conflict: {}", conflict.as_str());
    print_outputs(&outputs, ws_source);

    Ok(())
}