KeiSeiKit-1.0/_primitives/_rust/kei-refactor-engine/src/main.rs
Parfii-bot 19ee220e0a feat(primitives): 4 Rust crates for deep-sleep — conflict-scan, refactor-engine, graph-check, store
- kei-conflict-scan: rules/hooks/blocks/orphans/CP detection (6 tests)
- kei-refactor-engine: plan-mode + advisory patch format, zero-conflict guarantee (5 tests)
- kei-graph-check: wikilinks/handoffs/block-refs validator (4 tests)
- kei-store: trait + 5 backends (filesystem/github/forgejo/gitea prod, s3 stub) (8 tests)

1916 LOC Rust total; all files <200 LOC; 23/23 tests pass.
2026-04-22 08:28:22 +08:00

94 lines
2.7 KiB
Rust

//! kei-refactor-engine — binary entry.
//!
//! Usage:
//! kei-refactor-engine --input conflicts.json --plan-only > plan.md
//! kei-refactor-engine --input conflicts.json --apply-to-branch deep-sleep/2026-04-22 \
//! --plan-out plan.md --patch-out changes.patch
use anyhow::Result;
use clap::Parser;
use kei_refactor_engine::input::{read_conflicts, read_from_stdin};
use kei_refactor_engine::plan::Plan;
use kei_refactor_engine::{patch, render};
use std::path::PathBuf;
use std::process::ExitCode;
#[derive(Parser, Debug)]
#[command(name = "kei-refactor-engine", about = "Deep-sleep refactor-plan generator.")]
struct Cli {
/// Input JSON file (output of kei-conflict-scan). Use `-` for stdin.
#[arg(long)]
input: Option<PathBuf>,
/// Plan-only mode (default). Prints markdown to stdout if no --plan-out.
#[arg(long, default_value_t = true)]
plan_only: bool,
/// Apply mode — also write a patch file; takes the branch name.
#[arg(long)]
apply_to_branch: Option<String>,
/// Optional explicit path for the markdown plan.
#[arg(long)]
plan_out: Option<PathBuf>,
/// Optional explicit path for the patch file.
#[arg(long)]
patch_out: Option<PathBuf>,
}
fn load(cli: &Cli) -> Result<Vec<kei_refactor_engine::input::Conflict>> {
match cli.input.as_deref() {
None => read_from_stdin(),
Some(p) if p.to_string_lossy() == "-" => read_from_stdin(),
Some(p) => read_conflicts(p),
}
}
fn write_plan(plan: &Plan, branch: Option<&str>, out: Option<&PathBuf>) -> Result<()> {
let md = render::render(plan, branch);
match out {
Some(p) => std::fs::write(p, md)?,
None => print!("{}", md),
}
Ok(())
}
fn maybe_write_patch(
plan: &Plan,
branch: &str,
out: Option<&PathBuf>,
) -> Result<usize> {
let default = PathBuf::from("deep-sleep.patch");
let target = out.unwrap_or(&default);
patch::write_patch(plan, branch, target)
}
fn run(cli: &Cli) -> Result<ExitCode> {
let conflicts = load(cli)?;
let plan = Plan::from_conflicts(&conflicts);
let branch = cli.apply_to_branch.as_deref();
write_plan(&plan, branch, cli.plan_out.as_ref())?;
if let Some(br) = branch {
let applied = maybe_write_patch(&plan, br, cli.patch_out.as_ref())?;
eprintln!(
"kei-refactor-engine: wrote patch with {} auto-apply item(s); {} human-decision item(s) excluded.",
applied,
plan.manual_items().len(),
);
}
Ok(ExitCode::SUCCESS)
}
fn main() -> ExitCode {
let cli = Cli::parse();
match run(&cli) {
Ok(c) => c,
Err(e) => {
eprintln!("kei-refactor-engine: {e}");
ExitCode::from(1)
}
}
}