diff --git a/examples/custom-backend/main.rs b/examples/custom-backend/main.rs index 7bc5b6c6e..03579104c 100644 --- a/examples/custom-backend/main.rs +++ b/examples/custom-backend/main.rs @@ -17,7 +17,7 @@ use std::path::Path; use clap::{FromArgMatches, Subcommand}; use git2::Repository; -use jujutsu::cli_util::{handle_command_result, parse_args, CommandError, TracingSubscription}; +use jujutsu::cli_util::{parse_args, CliRunner, CommandError, TracingSubscription}; use jujutsu::commands::{default_app, run_command}; use jujutsu::config::read_config; use jujutsu::ui::Ui; @@ -64,13 +64,7 @@ fn run(ui: &mut Ui, tracing_subscription: &TracingSubscription) -> Result<(), Co } fn main() { - let tracing_subscription = TracingSubscription::init(); - jujutsu::cleanup_guard::init(); - let mut ui = Ui::new(); - let result = run(&mut ui, &tracing_subscription); - let exit_code = handle_command_result(&mut ui, result); - ui.finalize_writes(); - std::process::exit(exit_code); + CliRunner::init().set_dispatch_fn(run).run_and_exit(); } /// A commit backend that's extremely similar to the Git backend diff --git a/examples/custom-command/main.rs b/examples/custom-command/main.rs index ed83320c7..fb6fdb688 100644 --- a/examples/custom-command/main.rs +++ b/examples/custom-command/main.rs @@ -14,7 +14,7 @@ use clap::{FromArgMatches, Subcommand}; use jujutsu::cli_util::{ - handle_command_result, parse_args, short_commit_description, CommandError, TracingSubscription, + parse_args, short_commit_description, CliRunner, CommandError, TracingSubscription, }; use jujutsu::commands::{default_app, run_command}; use jujutsu::config::read_config; @@ -62,11 +62,5 @@ fn run(ui: &mut Ui, tracing_subscription: &TracingSubscription) -> Result<(), Co } fn main() { - let tracing_subscription = TracingSubscription::init(); - jujutsu::cleanup_guard::init(); - let mut ui = Ui::new(); - let result = run(&mut ui, &tracing_subscription); - let exit_code = handle_command_result(&mut ui, result); - ui.finalize_writes(); - std::process::exit(exit_code); + CliRunner::init().set_dispatch_fn(run).run_and_exit(); } diff --git a/src/cli_util.rs b/src/cli_util.rs index 792d875bc..f2eb697b7 100644 --- a/src/cli_util.rs +++ b/src/cli_util.rs @@ -1682,3 +1682,51 @@ pub fn handle_command_result(ui: &mut Ui, result: Result<(), CommandError>) -> i } } } + +/// CLI command builder and runner. +#[must_use] +pub struct CliRunner { + tracing_subscription: TracingSubscription, + dispatch_fn: F, +} + +impl CliRunner<()> { + /// Initializes CLI environment and returns a builder. This should be called + /// as early as possible. + pub fn init() -> Self { + let tracing_subscription = TracingSubscription::init(); + crate::cleanup_guard::init(); + CliRunner { + tracing_subscription, + dispatch_fn: (), + } + } + + // TODO: use crate::commands::run_command() by default + pub fn set_dispatch_fn(self, dispatch_fn: F) -> CliRunner + where + F: FnOnce(&mut Ui, &TracingSubscription) -> Result<(), CommandError>, + { + CliRunner { + tracing_subscription: self.tracing_subscription, + dispatch_fn, + } + } +} + +impl CliRunner +where + F: FnOnce(&mut Ui, &TracingSubscription) -> Result<(), CommandError>, +{ + pub fn run(self, ui: &mut Ui) -> Result<(), CommandError> { + (self.dispatch_fn)(ui, &self.tracing_subscription) + } + + pub fn run_and_exit(self) -> ! { + let mut ui = Ui::new(); + let result = self.run(&mut ui); + let exit_code = handle_command_result(&mut ui, result); + ui.finalize_writes(); + std::process::exit(exit_code); + } +} diff --git a/src/main.rs b/src/main.rs index 2befcfe8a..261c3d303 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use jujutsu::cli_util::{handle_command_result, parse_args, CommandError, TracingSubscription}; +use jujutsu::cli_util::{parse_args, CliRunner, CommandError, TracingSubscription}; use jujutsu::commands::{default_app, run_command}; use jujutsu::config::read_config; use jujutsu::ui::Ui; @@ -25,11 +25,5 @@ fn run(ui: &mut Ui, tracing_subscription: &TracingSubscription) -> Result<(), Co } fn main() { - let tracing_subscription = TracingSubscription::init(); - jujutsu::cleanup_guard::init(); - let mut ui = Ui::new(); - let result = run(&mut ui, &tracing_subscription); - let exit_code = handle_command_result(&mut ui, result); - ui.finalize_writes(); - std::process::exit(exit_code); + CliRunner::init().set_dispatch_fn(run).run_and_exit(); }