use std::io::Write;
use clap::Parser;
use mkit_core::layout::RepoLayout;
use mkit_core::refs::{self, Head};
use crate::clap_shim;
use crate::exit;
#[derive(Debug, Parser)]
#[command(
name = "mkit symbolic-ref",
about = "Read or write a symbolic ref (e.g. HEAD)."
)]
struct SymbolicRefOpts {
#[arg(long)]
short: bool,
name: String,
target: Option<String>,
}
#[must_use]
pub fn run(args: &[String]) -> u8 {
let opts = match clap_shim::parse::<SymbolicRefOpts>("mkit symbolic-ref", args) {
Ok(o) => o,
Err(code) => return code,
};
if opts.name != "HEAD" {
return emit_err(
&format!("only HEAD is a symbolic ref in mkit (got '{}')", opts.name),
exit::GENERAL_ERROR,
);
}
let cwd = match std::env::current_dir() {
Ok(p) => p,
Err(e) => return emit_err(&format!("cwd: {e}"), exit::NOINPUT),
};
let layout = match super::resolve_layout(&cwd) {
Ok(layout) => layout,
Err(code) => return code,
};
match opts.target {
Some(target) => write_head(&layout, &target),
None => read_head(&layout, opts.short),
}
}
fn read_head(layout: &RepoLayout, short: bool) -> u8 {
match refs::read_head(layout) {
Ok(Head::Branch(name)) => {
let mut stdout = std::io::stdout().lock();
if short {
let _ = writeln!(stdout, "{name}");
} else {
let _ = writeln!(stdout, "refs/heads/{name}");
}
exit::OK
}
Ok(Head::Detached(_)) => emit_err("ref HEAD is not a symbolic ref", exit::GENERAL_ERROR),
Err(e) => emit_err(&format!("read HEAD: {e}"), exit::DATAERR),
}
}
fn write_head(layout: &RepoLayout, target: &str) -> u8 {
let Some(branch) = target.strip_prefix("refs/heads/") else {
return emit_err(
&format!("HEAD can only point at a branch under refs/heads/ (got '{target}')"),
exit::USAGE,
);
};
if !refs::validate_ref_name(branch) {
return emit_err(&format!("invalid branch name '{branch}'"), exit::USAGE);
}
match refs::write_head_branch(layout, branch) {
Ok(()) => exit::OK,
Err(e) => emit_err(&format!("write HEAD: {e}"), exit::CANTCREAT),
}
}
use super::error as emit_err;