1#![deny(unsafe_code)]
18
19pub mod clap_shim;
20pub mod cli;
21pub mod commands;
22pub mod config;
23pub mod editor;
24pub mod exit;
25pub mod format;
26pub mod remote_dispatch;
27pub mod signal;
28#[cfg(feature = "sparse-checkout")]
29pub mod sparse_cache;
30pub mod term;
31
32use std::io::Write;
33
34#[must_use]
43pub fn dispatch(argv: &[String]) -> u8 {
44 if argv.len() < 2 {
45 print_usage_stderr();
46 return exit::USAGE;
47 }
48 let cmd = &argv[1];
49 let rest: Vec<String> = argv.iter().skip(2).cloned().collect();
50
51 match cmd.as_str() {
52 "-h" | "--help" | "help" => {
53 let mut stdout = std::io::stdout().lock();
54 let _ = stdout.write_all(cli::HELP_TEXT.as_bytes());
55 exit::OK
56 }
57 "version" | "--version" | "-V" => {
58 let mut stdout = std::io::stdout().lock();
59 let _ = writeln!(stdout, "mkit {}", cli::CLI_VERSION);
67 exit::OK
68 }
69 "init" => commands::init::run(&rest),
70 "key" => commands::key::run(&rest),
71 "keygen" => commands::keygen::run(&rest),
72 "hash" => commands::hash_cmd::run(&rest),
73 "cat" => commands::cat::run(&rest),
74 "cat-file" => commands::cat_file::run(&rest),
75 "ls-tree" => commands::ls_tree::run(&rest),
76 "ls-files" => commands::ls_files::run(&rest),
77 "rev-parse" => commands::rev_parse::run(&rest),
78 "show" => commands::show::run(&rest),
79 "show-ref" => commands::show_ref::run(&rest),
80 "for-each-ref" => commands::for_each_ref::run(&rest),
81 "symbolic-ref" => commands::symbolic_ref::run(&rest),
82 "update-ref" => commands::update_ref::run(&rest),
83 "tree" => commands::tree::run(&rest),
84 "add" => commands::add::run(&rest),
85 "rm" => commands::rm::run(&rest),
86 "mv" => commands::mv::run(&rest),
87 "restore" => commands::restore::run(&rest),
88 "reset" => commands::reset::run(&rest),
89 "status" => commands::status::run(&rest),
90 "commit" => commands::commit::run(&rest),
91 "log" => commands::log::run(&rest),
92 "reflog" => commands::reflog::run(&rest),
93 "branch" => commands::branch::run(&rest),
94 "tag" => commands::tag::run(&rest),
95 "checkout" => commands::checkout::run(&rest),
96 "clean" => commands::clean::run(&rest),
97 "diff" => commands::diff::run(&rest),
98 "verify" => commands::verify::run(&rest),
99 "attest" => commands::attest::run(&rest),
100 "verify-attest" => commands::verify_attest::run(&rest),
101 "config" => commands::config_cmd::run(&rest),
102 "remote" => commands::remote::run(&rest),
103 "push" => commands::push::run(&rest),
104 "pull" => commands::pull::run(&rest),
105 "fetch" => commands::fetch::run(&rest),
106 "clone" => commands::clone::run(&rest),
107 "mcp" => commands::mcp::run(&rest),
108 "merge" => commands::merge::run(&rest),
109 "cherry-pick" => commands::cherry_pick::run(&rest),
110 "revert" => commands::revert::run(&rest),
111 "rebase" => commands::rebase::run(&rest),
112 "bisect" => commands::bisect::run(&rest),
113 "gc" => commands::gc::run(&rest),
114 "stash" => commands::stash::run(&rest),
115 "blame" => commands::blame::run(&rest),
116 "serve" => commands::serve::run(&rest),
117 #[cfg(feature = "git-bridge")]
118 "git" => commands::git::run(&rest),
119 #[cfg(not(feature = "git-bridge"))]
120 "git" => {
121 let mut stderr = std::io::stderr().lock();
122 let _ = writeln!(
123 stderr,
124 "error: the git bridge is not compiled into this binary; \
125 rebuild with `--features git-bridge` (see docs/SPEC-GIT-BRIDGE.md)"
126 );
127 exit::UNAVAILABLE
128 }
129 "sparse-checkout" => commands::sparse_checkout::run(&rest),
130 #[cfg(feature = "pack-shards")]
131 "pack-shard" => commands::pack_shard::run(&rest),
132 other => {
133 let mut stderr = std::io::stderr().lock();
134 let _ = writeln!(
135 stderr,
136 "error: unknown command '{other}' (run 'mkit --help' for a list of commands)"
137 );
138 exit::USAGE
139 }
140 }
141}
142
143fn print_usage_stderr() {
144 let mut stderr = std::io::stderr().lock();
145 let _ = stderr.write_all(cli::HELP_TEXT.as_bytes());
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn dispatch_version_returns_ok() {
154 let argv = vec!["mkit".to_string(), "version".to_string()];
156 assert_eq!(dispatch(&argv), exit::OK);
157 }
158
159 #[test]
160 fn dispatch_unknown_command_returns_usage() {
161 let argv = vec!["mkit".to_string(), "definitely-not-a-command".to_string()];
162 assert_eq!(dispatch(&argv), exit::USAGE);
163 }
164
165 #[test]
166 fn dispatch_bare_binary_returns_usage() {
167 let argv = vec!["mkit".to_string()];
168 assert_eq!(dispatch(&argv), exit::USAGE);
169 }
170}