Skip to main content

mkit_cli/commands/
hash_cmd.rs

1//! `mkit hash <file>` — hash a file as a blob, store it, print the hash.
2
3use std::io::Write;
4
5use clap::Parser;
6use mkit_core::object::{Blob, Object};
7use mkit_core::serialize;
8use mkit_core::store::ObjectStore;
9use mkit_core::worktree;
10
11use crate::clap_shim;
12use crate::exit;
13use crate::format;
14
15#[derive(Debug, Parser)]
16#[command(name = "mkit hash", about = "Hash a file as a blob and store it.")]
17struct HashOpts {
18    /// File to hash and store.
19    file: String,
20}
21
22#[must_use]
23pub fn run(args: &[String]) -> u8 {
24    let opts = match clap_shim::parse::<HashOpts>("mkit hash", args) {
25        Ok(o) => o,
26        Err(code) => return code,
27    };
28    let path = &opts.file;
29    let cwd = match std::env::current_dir() {
30        Ok(p) => p,
31        Err(e) => return emit_err(&format!("cwd: {e}"), exit::NOINPUT),
32    };
33    let store = match ObjectStore::open(&cwd) {
34        Ok(s) => s,
35        Err(e) => return emit_err(&format!("not a mkit repo: {e}"), exit::GENERAL_ERROR),
36    };
37    // Bounded + symlink-safe read (#223): refuse to follow a final
38    // symlink and cap the size at MAX_FILE_BYTES rather than slurping an
39    // arbitrarily large file (or a symlink target outside the repo)
40    // through `std::fs::read`.
41    let bytes = match worktree::read_regular_file_bounded(std::path::Path::new(path)) {
42        Ok((_, data)) => data,
43        Err(e) => return emit_err(&format!("read {path}: {e}"), exit::NOINPUT),
44    };
45    let blob = Object::Blob(Blob { data: bytes });
46    let serialized = match serialize::serialize(&blob) {
47        Ok(b) => b,
48        Err(e) => return emit_err(&format!("serialize: {e}"), exit::DATAERR),
49    };
50    match store.write(&serialized) {
51        Ok(h) => {
52            let mut stdout = std::io::stdout().lock();
53            let _ = writeln!(stdout, "{}", format::hex_hash(&h));
54            exit::OK
55        }
56        Err(e) => emit_err(&format!("store: {e}"), exit::CANTCREAT),
57    }
58}
59
60fn emit_err(msg: &str, code: u8) -> u8 {
61    let mut stderr = std::io::stderr().lock();
62    let _ = writeln!(stderr, "error: {msg}");
63    code
64}