From 7480cdcf96619fb92cb414ca4c8afc693b52d8c5 Mon Sep 17 00:00:00 2001 From: J / Jacob Babich Date: Wed, 16 Oct 2024 02:07:57 -0400 Subject: [PATCH] feat(multibinary): support running the binary without arguments (using a sensible user interface as the default), and improve error reporting by using `snafu` --- multibinary/src/main.rs | 107 +++++++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/multibinary/src/main.rs b/multibinary/src/main.rs index ddc5c9f..50d73d6 100644 --- a/multibinary/src/main.rs +++ b/multibinary/src/main.rs @@ -1,6 +1,12 @@ -use std::{fs::create_dir_all, io::ErrorKind, path::PathBuf}; +use std::{ + fs::create_dir_all, + io::ErrorKind, + path::{Path, PathBuf}, +}; +use cfg_if::cfg_if; use clap::{Parser, Subcommand}; +use snafu::{ResultExt, Snafu}; #[derive(Debug, Parser)] struct Args { @@ -11,24 +17,91 @@ struct Args { )] application_data_directory: PathBuf, + // If there is a GUI or TUI available from this binary, + // then calling this program without arguments + // is acceptable: it will launch a user interface + #[cfg(any(feature = "gui-eframe", feature = "tui-ratatui"))] + #[command(subcommand)] + command: Option, + + #[cfg(not(any(feature = "gui-eframe", feature = "tui-ratatui")))] #[command(subcommand)] command: Command, } #[derive(Debug, Subcommand)] enum Command { - #[cfg(feature = "cli-clap")] - #[command(alias = "cli")] - CliClap(ac_qu_ai_nt_cli_clap::Args), #[cfg(feature = "gui-eframe")] #[command(alias = "gui")] GuiEframe, + #[cfg(feature = "tui-ratatui")] #[command(alias = "tui")] TuiRatatui, + + #[cfg(feature = "cli-clap")] + #[command(alias = "cli")] + CliClap(ac_qu_ai_nt_cli_clap::Args), } -fn main() { +// When this program is run without arguments, +// it will launch a user interface +cfg_if!( + // with the GUI (made with eframe) + // being considered more appealing (made the default) + if #[cfg(feature = "gui-eframe")] { + impl Default for Command { + fn default() -> Self { + Command::GuiEframe + } + } + } + // than the TUI (made with Ratatui) + else if #[cfg(feature = "tui-ratatui")] { + impl Default for Command { + fn default() -> Self { + Command::TuiRatatui + } + } + } + // with it not being logical to specify + // the CLI (made with clap) + // as an option, + // because if `ac-qu-ai-nt ask "why is the sky blue?"` + // were accepted and worked when `cli-clap` was the only + // interface enabled, then when another interface + // like `gui-eframe` were enabled, it would stop working + // (only able to work as `ac-qu-ai-nt cli-clap ask "why is the sky blue?"`) + // so it should be required to do it the way that would work + // in both cases from the beginning +); + +#[derive(Debug, Snafu)] +enum ApplicationError { + #[snafu(display("failed to create the {path:?} directory"))] + DirectoryCreationError { + path: PathBuf, + source: std::io::Error, + }, +} + +/// Create the directory and its parents, +/// but don't return an error if it already exists and is a directory +fn create_dir_all_exist_ok(path: impl AsRef) -> Result<(), std::io::Error> { + match create_dir_all(&path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == ErrorKind::AlreadyExists && path.as_ref().is_dir() { + Ok(()) + } else { + Err(e) + } + } + } +} + +#[snafu::report] +fn main() -> Result<(), ApplicationError> { let Args { application_data_directory, command, @@ -37,23 +110,19 @@ fn main() { #[cfg(feature = "tracing")] tracing_subscriber::fmt::init(); - match create_dir_all(&application_data_directory) { - Ok(()) => {} - Err(e) if e.kind() == ErrorKind::AlreadyExists => {} - Err(e) => { - panic!("{}", e); + create_dir_all_exist_ok(&application_data_directory).with_context(|_| { + DirectoryCreationSnafu { + path: application_data_directory.clone(), } - } + })?; let tracing_directory = application_data_directory.join("logs"); + create_dir_all_exist_ok(&tracing_directory).with_context(|_| DirectoryCreationSnafu { + path: tracing_directory.clone(), + })?; - match create_dir_all(&tracing_directory) { - Ok(()) => {} - Err(e) if e.kind() == ErrorKind::AlreadyExists => {} - Err(e) => { - panic!("{}", e); - } - } + #[cfg(any(feature = "gui-eframe", feature = "tui-ratatui"))] + let command = command.unwrap_or_default(); match command { #[cfg(feature = "cli-clap")] @@ -63,4 +132,6 @@ fn main() { #[cfg(feature = "tui-ratatui")] Command::TuiRatatui => ac_qu_ai_nt_tui_ratatui::main(), } + + Ok(()) }