1use std::io::Write;
4
5use clap::Parser;
6use mkit_core::hash::from_hex;
7use mkit_core::object::Object;
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 cat", about = "Display an object by its hash.")]
17struct CatOpts {
18 hash: String,
20}
21
22#[must_use]
23pub fn run(args: &[String]) -> u8 {
24 let opts = match clap_shim::parse::<CatOpts>("mkit cat", args) {
25 Ok(o) => o,
26 Err(code) => return code,
27 };
28 let hash_hex = &opts.hash;
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 layout = match super::resolve_layout(&cwd) {
34 Ok(layout) => layout,
35 Err(code) => return code,
36 };
37 let store = match ObjectStore::open(&layout) {
38 Ok(s) => s,
39 Err(e) => return emit_err(&format!("not a mkit repo: {e}"), exit::GENERAL_ERROR),
40 };
41 let h = match from_hex(hash_hex) {
42 Ok(h) => h,
43 Err(e) => return emit_err(&format!("bad hash: {e}"), exit::DATAERR),
44 };
45 let obj = match store.read_object(&h) {
46 Ok(o) => o,
47 Err(e) => return emit_err(&format!("read: {e}"), exit::NOINPUT),
48 };
49 let mut stdout = std::io::stdout().lock();
50 match obj {
51 Object::Blob(b) => {
52 let _ = stdout.write_all(&b.data);
53 }
54 Object::ChunkedBlob(_) => match worktree::read_blob(&store, &h) {
58 Ok(data) => {
59 let _ = stdout.write_all(&data);
60 }
61 Err(e) => return emit_err(&format!("reassemble chunked blob: {e}"), exit::NOINPUT),
62 },
63 Object::Tree(t) => {
64 for e in t.entries {
65 let _ = writeln!(
66 stdout,
67 "{:02x} {} {}",
68 e.mode as u8,
69 format::hex_hash(&e.object_hash),
70 String::from_utf8_lossy(&e.name)
71 );
72 }
73 }
74 Object::Commit(c) => {
75 let _ = writeln!(stdout, "tree {}", format::hex_hash(&c.tree_hash));
76 for p in &c.parents {
77 let _ = writeln!(stdout, "parent {}", format::hex_hash(p));
78 }
79 let _ = writeln!(stdout, "author {}", format::short_identity(&c.author));
80 let _ = writeln!(stdout, "timestamp {}", c.timestamp);
81 let _ = writeln!(stdout);
82 let _ = stdout.write_all(&c.message);
83 let _ = writeln!(stdout);
84 }
85 Object::Tag(t) => {
86 let _ = writeln!(stdout, "object {}", format::hex_hash(&t.target));
87 let _ = writeln!(stdout, "type {}", t.target_type.name());
88 let _ = writeln!(stdout, "tag {}", String::from_utf8_lossy(&t.name));
89 let _ = writeln!(stdout, "tagger {}", format::short_identity(&t.tagger));
90 let _ = writeln!(stdout, "timestamp {}", t.timestamp);
91 let signed = t.signature != [0u8; 64];
92 let _ = writeln!(stdout, "signed {signed}");
93 let _ = writeln!(stdout);
94 let _ = stdout.write_all(&t.message);
95 let _ = writeln!(stdout);
96 }
97 other => {
98 let _ = writeln!(stdout, "{other}");
99 }
100 }
101 exit::OK
102}
103
104use super::error as emit_err;