mkit_cli/commands/
symbolic_ref.rs1use std::io::Write;
13
14use clap::Parser;
15use mkit_core::layout::RepoLayout;
16use mkit_core::refs::{self, Head};
17
18use crate::clap_shim;
19use crate::exit;
20
21#[derive(Debug, Parser)]
22#[command(
23 name = "mkit symbolic-ref",
24 about = "Read or write a symbolic ref (e.g. HEAD)."
25)]
26struct SymbolicRefOpts {
27 #[arg(long)]
29 short: bool,
30 name: String,
32 target: Option<String>,
35}
36
37#[must_use]
38pub fn run(args: &[String]) -> u8 {
39 let opts = match clap_shim::parse::<SymbolicRefOpts>("mkit symbolic-ref", args) {
40 Ok(o) => o,
41 Err(code) => return code,
42 };
43 if opts.name != "HEAD" {
44 return emit_err(
45 &format!("only HEAD is a symbolic ref in mkit (got '{}')", opts.name),
46 exit::GENERAL_ERROR,
47 );
48 }
49 let cwd = match std::env::current_dir() {
50 Ok(p) => p,
51 Err(e) => return emit_err(&format!("cwd: {e}"), exit::NOINPUT),
52 };
53 let layout = match super::resolve_layout(&cwd) {
54 Ok(layout) => layout,
55 Err(code) => return code,
56 };
57
58 match opts.target {
59 Some(target) => write_head(&layout, &target),
60 None => read_head(&layout, opts.short),
61 }
62}
63
64fn read_head(layout: &RepoLayout, short: bool) -> u8 {
66 match refs::read_head(layout) {
67 Ok(Head::Branch(name)) => {
68 let mut stdout = std::io::stdout().lock();
69 if short {
70 let _ = writeln!(stdout, "{name}");
71 } else {
72 let _ = writeln!(stdout, "refs/heads/{name}");
73 }
74 exit::OK
75 }
76 Ok(Head::Detached(_)) => emit_err("ref HEAD is not a symbolic ref", exit::GENERAL_ERROR),
78 Err(e) => emit_err(&format!("read HEAD: {e}"), exit::DATAERR),
79 }
80}
81
82fn write_head(layout: &RepoLayout, target: &str) -> u8 {
85 let Some(branch) = target.strip_prefix("refs/heads/") else {
86 return emit_err(
87 &format!("HEAD can only point at a branch under refs/heads/ (got '{target}')"),
88 exit::USAGE,
89 );
90 };
91 if !refs::validate_ref_name(branch) {
92 return emit_err(&format!("invalid branch name '{branch}'"), exit::USAGE);
93 }
94 match refs::write_head_branch(layout, branch) {
95 Ok(()) => exit::OK,
96 Err(e) => emit_err(&format!("write HEAD: {e}"), exit::CANTCREAT),
97 }
98}
99
100use super::error as emit_err;