1pub mod agent;
13pub(crate) mod cli;
14pub(crate) mod commands;
15pub mod config;
16pub mod copy;
17pub mod cx;
18pub mod error;
19pub mod gh;
20pub mod git;
21pub mod hooks;
22pub mod keys;
23pub mod model;
24pub mod output;
25pub mod query;
26pub mod slug;
27pub mod template;
28pub mod time;
29pub mod tui;
30pub mod util;
31pub mod version;
32pub(crate) mod worktree_service;
33
34#[cfg(test)]
35mod testutil;
36
37pub use cx::{Cx, Env, Stream};
38pub use error::{Error, Result};
39
40pub fn run(args: Vec<String>, cx: &mut Cx) -> u8 {
43 let result = cli::dispatch(args, cx);
44 finish(result, &mut cx.err)
45}
46
47fn finish(result: Result<u8>, err: &mut Stream) -> u8 {
49 match result {
50 Ok(code) => code,
51 Err(e) => {
52 let _ = err.line(&format!("error: {e}"));
53 e.exit_code()
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::testutil::test_cx;
62
63 #[test]
64 fn finish_passes_through_success_code() {
65 let mut t = test_cx(&[], "/tmp");
66 assert_eq!(finish(Ok(0), &mut t.cx.err), 0);
67 assert_eq!(finish(Ok(1), &mut t.cx.err), 1);
68 assert!(t.err.contents().is_empty());
69 }
70
71 #[test]
72 fn finish_reports_error_to_stderr_and_maps_code() {
73 let mut t = test_cx(&[], "/tmp");
74 let code = finish(Err(Error::usage("bad flag")), &mut t.cx.err);
75 assert_eq!(code, 2);
76 assert_eq!(t.err.contents(), "error: bad flag\n");
77 assert!(t.out.contents().is_empty());
78 }
79
80 #[test]
81 fn run_help_exits_zero_via_clap() {
82 let mut t = test_cx(&[], "/tmp");
83 assert_eq!(run(vec!["--help".to_string()], &mut t.cx), 0);
84 assert!(t.out.contents().contains("Usage"));
85 }
86
87 #[test]
88 fn run_maps_command_error_to_exit_code() {
89 let mut t = test_cx(&[], "/tmp");
92 assert_eq!(run(vec![], &mut t.cx), 1);
93 assert!(t.err.contents().contains("not in a git repository"));
94 }
95}