diff --git a/Cargo.toml b/Cargo.toml index 31a423d..0688588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ snafu = "0.8" # Data lazy_static = "1.4" serde = { version = "1.0", features = ["derive"] } -serde-aux = "4.2" +serde-aux = "4.5" serde_yaml = "0.9" tracing-serde = "0.1" speedy = "0.8" @@ -63,12 +63,12 @@ log = "0.4" fluent = "0.16" # CLI -clap = { version = "4.4", features = ["derive", "wrap_help"] } -clap-verbosity-flag = "2.0" +clap = { version = "4.5", features = ["derive", "wrap_help"] } +clap-verbosity-flag = "2.2" anstream = "0.6" owo-colors = "4.0" console = "0.15" # GUI -relm4 = "0.6" -relm4-components = "0.6" \ No newline at end of file +relm4 = "0.8" +relm4-components = "0.8" \ No newline at end of file diff --git a/paxy-cli/src/lib.rs b/paxy-cli/src/lib.rs index 59cf00e..50218c0 100644 --- a/paxy-cli/src/lib.rs +++ b/paxy-cli/src/lib.rs @@ -27,109 +27,254 @@ use snafu::Snafu; // region: MODULES -/// Groups together all the data structures that make up the `derive` interface -/// of the `clap` library. This is separate from any of the helper functions -/// that are used as part of the commandline interface. +/// This module is a [*derive* interface template](https://docs.rs/clap/latest/clap/_derive/_tutorial/chapter_0/index.html) specifically for +/// use with the `clap` library. Any other commandline-related code that is not +/// part of the `clap` derive template will not be in this module. +/// The CLI is designed to (as much as possible,) follow the guidelines in +/// https://clig.dev/ . As a consequence, the command structure follows the +/// 'application_name noun verb' order of subcommands. For example: +/// `paxy package list [args]`, `paxy repo add [args]` mod cli_template { - #[derive(Parser, Debug)] - #[command(version, author, about, args_conflicts_with_subcommands = true)] - pub struct CliTemplate { - #[clap(flatten)] - pub global_args: GlobalArgs, - #[clap(subcommand)] - pub command: Option, + /// The base commandline template consists of global arguments, a subcommand + /// that denotes the entity that is being operated upon (like a package or + /// repository), and optionally, arguments for the default subcommand (in + /// this case, the 'package' entity is assumed chosen to act on, by + /// default). + #[derive(Debug, Parser)] + #[command( + version, + author, + about, + args_conflicts_with_subcommands = true, + propagate_version = true + )] + pub struct CliTemplate { + #[command(flatten)] + pub global_arguments: GlobalArguments, - #[clap(flatten)] - pub arguments: ListActionArguments, + #[command(subcommand)] + pub entity: Option, } + /// Implement a trait that can extract standard global arguments from our + /// own CLI template impl ui::GlobalArguments for CliTemplate { type L = clap_verbosity_flag::InfoLevel; fn config_file(&self) -> &Option { &self - .global_args + .global_arguments .config_file } fn is_json(&self) -> bool { - self.global_args + self.global_arguments .json_flag } fn is_plain(&self) -> bool { - self.global_args + self.global_arguments .plain_flag } fn is_debug(&self) -> bool { - self.global_args + self.global_arguments .debug_flag } fn is_no_color(&self) -> bool { - self.global_args + self.global_arguments .no_color_flag } fn is_test(&self) -> bool { - self.global_args + self.global_arguments .test_flag } fn verbosity(&self) -> &clap_verbosity_flag::Verbosity { &self - .global_args + .global_arguments .verbose } } + #[derive(Clone, Debug, Args)] + #[command(next_display_order = usize::MAX - 100)] + pub struct GlobalArguments + where + L: clap_verbosity_flag::LogLevel, + { + #[arg( + long = "config", + short = 'c', + help = "Path to the configuration file to use.", + global = true, + display_order = usize::MAX - 6 + )] + pub config_file: Option, + + #[arg( + long = "json", + help = "Output in the JSON format for machine readability and scripting purposes.", + global = true, + display_order = usize::MAX - 5 + )] + pub json_flag: bool, + + #[arg( + long = "plain", + help = "Output as plain text without extra information, for machine readability and scripting purposes.", + global = true, + display_order = usize::MAX - 4 + )] + pub plain_flag: bool, + + #[arg( + long = "debug", + help = "Output debug messages.", + global = true, + display_order = usize::MAX - 3 + )] + pub debug_flag: bool, + + #[arg( + long = "no-color", + help = "Disable output coloring.", + global = true, + display_order = usize::MAX - 2 + )] + pub no_color_flag: bool, + + #[arg( + long = "test", + help = "Avoid destructive modifications and show all output subject to the commandline filters. Useful for dry-runs and for developers.", + global = true, + display_order = usize::MAX - 1 + )] + pub test_flag: bool, + + #[command(flatten)] + pub verbose: clap_verbosity_flag::Verbosity, + } + #[derive(Debug, Subcommand)] - #[clap(args_conflicts_with_subcommands = true)] - pub enum ActionCommand { - #[clap(name = "list", about = "List installed packages.", display_order = 1)] - List(ListActionArguments), + #[command(args_conflicts_with_subcommands = true)] + pub enum EntitySubcommand { + #[command( + name = "package", + about = "Perform actions on package(s).", + subcommand, + display_order = 1 + )] + Package(PackageSubcommand), - #[clap( + #[command( + subcommand, + name = "repository", + alias = "repo", + about = "Perform actions on repository(-ies).", + display_order = 2 + )] + Repository(RepositorySubcommand), + } + + #[derive(Debug, Subcommand)] + #[command(args_conflicts_with_subcommands = true)] + pub enum PackageSubcommand { + #[command(name = "list", about = "List installed packages.", display_order = 1)] + List(PackageListArguments), + + #[command( name = "search", alias = "find", about = "Search for available packages.", display_order = 2 )] - Search(SearchActionArguments), + Search(PackageSearchArguments), - #[clap( + #[command( name = "install", alias = "add", about = "Install packages.", display_order = 3 )] - Install(InstallActionArguments), + Install(PackageInstallArguments), - #[clap( + #[command( name = "update", alias = "upgrade", about = "Update packages.", display_order = 4 )] - Update(UpdateActionArguments), + Update(PackageUpdateArguments), - #[clap( + #[command( name = "uninstall", alias = "remove", about = "Uninstall packages.", display_order = 5 )] - Uninstall(UninstallActionArguments), + Uninstall(PackageUninstallArguments), + + #[command(name = "downgrade", about = "Downgrade a package.", display_order = 5)] + Downgrade(PackageDowngradeArguments), + } - #[clap(name = "downgrade", about = "Downgrade a package.", display_order = 5)] - Downgrade(DowngradeActionArguments), + #[derive(Debug, Subcommand)] + #[command(args_conflicts_with_subcommands = true)] + pub enum RepositorySubcommand { + #[command( + name = "list", + about = "List installed repositories.", + display_order = 1 + )] + List(RepositoryListArguments), + + #[command( + name = "search", + alias = "find", + about = "Search for available repositories.", + display_order = 2 + )] + Search(RepositorySearchArguments), + + #[command( + name = "install", + alias = "add", + about = "Install repositories.", + display_order = 3 + )] + Install(RepositoryInstallArguments), + + #[command( + name = "update", + alias = "upgrade", + about = "Update repositories.", + display_order = 4 + )] + Update(RepositoryUpdateArguments), + + #[command( + name = "uninstall", + alias = "remove", + about = "Uninstall repositories.", + display_order = 5 + )] + Uninstall(RepositoryUninstallArguments), + + #[command( + name = "downgrade", + about = "Downgrade a repositories.", + display_order = 5 + )] + Downgrade(RepositoryDowngradeArguments), } #[derive(Debug, Args)] - pub struct ListActionArguments { - #[clap( + pub struct PackageListArguments { + #[arg( long = "exclude", alias = "ignore", short = 'e', @@ -138,16 +283,16 @@ mod cli_template { )] pub excluded_partial_package_names: Vec, - #[clap( + #[arg( help = "Partial or full name(s) of the packages to search among the installed packages. Not specifying this argument will list all packages.", - display_order = usize::MAX - 1 + display_order = usize::MAX - 1, )] - pub partial_package_name: Option, // This should always be the last argument + pub partial_package_name: Vec, } #[derive(Debug, Args)] - pub struct SearchActionArguments { - #[clap( + pub struct PackageSearchArguments { + #[arg( long = "exclude", alias = "ignore", short = 'e', @@ -156,22 +301,23 @@ mod cli_template { )] pub excluded_partial_package_names: Vec, - #[clap( + #[arg( help = "Partial or full name(s) of the packages to search among available packages.", + last = true, display_order = usize::MAX - 1 )] - pub partial_package_name: String, // This should always be the last argument + pub partial_package_name: Vec, } #[derive(Debug, Args)] - pub struct InstallActionArguments { - #[clap(help = "Full name(s) of the packages to install.", display_order = usize::MAX - 1)] - pub package_names: Vec, // This should always be the last argument + pub struct PackageInstallArguments { + #[arg(help = "Full name(s) of the packages to install.", display_order = usize::MAX - 1)] + pub package_names: Vec, } #[derive(Debug, Args)] - pub struct UpdateActionArguments { - #[clap( + pub struct PackageUpdateArguments { + #[arg( long = "exclude", alias = "ignore", short = 'e', @@ -180,25 +326,27 @@ mod cli_template { )] pub excluded_package_names: Vec, - #[clap( + #[arg( help = "Full name(s) of the packages to update. Not specifying this argument will update all packages", + last = true, display_order = usize::MAX - 1 )] - pub package_names: Vec, // This should always be the last argument + pub package_names: Vec, } #[derive(Debug, Args)] - pub struct UninstallActionArguments { - #[clap( + pub struct PackageUninstallArguments { + #[arg( help = "Full name(s) of the packages to uninstall.", + last = true, display_order = usize::MAX - 1 )] - pub package_names: Vec, // This should always be the last argument + pub package_names: Vec, } #[derive(Debug, Args)] - pub struct DowngradeActionArguments { - #[clap( + pub struct PackageDowngradeArguments { + #[arg( long = "version", alias = "ver", help = "The version to downgrade to.", @@ -206,11 +354,103 @@ mod cli_template { )] pub version: Option, - #[clap( + #[arg( help = "Full name of the package to downgrade.", + last = true, + display_order = usize::MAX - 1 + )] + pub package_name: String, + } + + #[derive(Debug, Args)] + pub struct RepositoryListArguments { + #[arg( + long = "exclude", + alias = "ignore", + short = 'e', + help = "Partial or full name(s) of repositories to exclude from the search among the installed repositories.", + display_order = 1 + )] + pub excluded_partial_repository_names: Vec, + + #[arg( + help = "Partial or full name(s) of the repositories to search among the installed repositories. Not specifying this argument will list all repositories.", + last = true, + display_order = usize::MAX - 1, + )] + pub partial_repository_name: Vec, + } + + #[derive(Debug, Args)] + pub struct RepositorySearchArguments { + #[arg( + long = "exclude", + alias = "ignore", + short = 'e', + help = "Partial or full name(s) of repositories to exclude from the search among available repositories.", + display_order = 1 + )] + pub excluded_partial_repository_names: Vec, + + #[arg( + help = "Partial or full name(s) of the repositories to search among available repositories.", + last = true, + display_order = usize::MAX - 1 + )] + pub partial_repository_name: String, + } + + #[derive(Debug, Args)] + pub struct RepositoryInstallArguments { + #[arg(help = "Full name(s) of the repositories to install.", display_order = usize::MAX - 1)] + pub repository_names: Vec, + } + + #[derive(Debug, Args)] + pub struct RepositoryUpdateArguments { + #[arg( + long = "exclude", + alias = "ignore", + short = 'e', + help = "Full name(s) of repositories to exclude from updating.", + display_order = 1 + )] + pub excluded_repository_names: Vec, + + #[arg( + help = "Full name(s) of the repositories to update. Not specifying this argument will update all repositories", + last = true, + display_order = usize::MAX - 1 + )] + pub repository_names: Vec, + } + + #[derive(Debug, Args)] + pub struct RepositoryUninstallArguments { + #[arg( + help = "Full name(s) of the repositories to uninstall.", + last = true, + display_order = usize::MAX - 1 + )] + pub repository_names: Vec, + } + + #[derive(Debug, Args)] + pub struct RepositoryDowngradeArguments { + #[arg( + long = "version", + alias = "ver", + help = "The version to downgrade to.", + display_order = 1 + )] + pub version: Option, + + #[arg( + help = "Full name of the repository to downgrade.", + last = true, display_order = usize::MAX - 1 )] - pub package_name: String, // This should always be the last argument + pub repository_name: String, } // region: IMPORTS @@ -218,7 +458,7 @@ mod cli_template { use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; - use paxy::ui::{self, GlobalArgs}; + use paxy::ui; // endregion: IMPORTS } diff --git a/paxy/src/ui/mod.rs b/paxy/src/ui/mod.rs index 00dfc17..39ba637 100644 --- a/paxy/src/ui/mod.rs +++ b/paxy/src/ui/mod.rs @@ -96,15 +96,17 @@ where "███".bright_white() ); - // Test messages - tracing::trace!(target:"TEST", "{} Testing trace!...", console::Emoji("🧪", "")); - tracing::debug!(target:"TEST", "{} Testing debug!...", console::Emoji("🧪", "")); - tracing::info!(target:"TEST", "{} Testing info!...", console::Emoji("🧪", "")); - tracing::warn!(target:"TEST", "{} Testing warn!...", console::Emoji("🧪", "")); - tracing::error!(target:"TEST", "{} Testing error!...", console::Emoji("🧪", "")); - - tracing::info!(target:"JSON", "{} Testing: {}", console::Emoji("🧪", ""), "{\"JSON\": \"Target\"}"); - tracing::info!(target:"PLAIN", "{} Testing: Plain Target", console::Emoji("🧪", "")); + if cli_input.is_test() { + // Test messages + tracing::trace!(target:"TEST", "{} Testing trace!...", console::Emoji("🧪", "")); + tracing::debug!(target:"TEST", "{} Testing debug!...", console::Emoji("🧪", "")); + tracing::info!(target:"TEST", "{} Testing info!...", console::Emoji("🧪", "")); + tracing::warn!(target:"TEST", "{} Testing warn!...", console::Emoji("🧪", "")); + tracing::error!(target:"TEST", "{} Testing error!...", console::Emoji("🧪", "")); + + tracing::info!(target:"JSON", "{} Testing: {}", console::Emoji("🧪", ""), "{\"JSON\": \"Target\"}"); + tracing::info!(target:"PLAIN", "{} Testing: Plain Target", console::Emoji("🧪", "")); + } tracing::debug!( "{} The {} is {}... {}", @@ -237,81 +239,8 @@ use crate::app::{self, config, logging}; // region: MODULES -mod cli_template { - #[derive(Clone, Debug, Args)] - #[clap(args_conflicts_with_subcommands = true, next_display_order = usize::MAX - 100)] - pub struct GlobalArgs - where - L: clap_verbosity_flag::LogLevel, - { - #[clap( - long = "config", - short = 'c', - help = "Path to the configuration file to use.", - global = true, - display_order = usize::MAX - 6 - )] - pub config_file: Option, - - #[clap( - long = "json", - help = "Output in the JSON format for machine readability and scripting purposes.", - global = true, - display_order = usize::MAX - 5 - )] - pub json_flag: bool, - - #[clap( - long = "plain", - help = "Output as plain text without extra information, for machine readability and scripting purposes.", - global = true, - display_order = usize::MAX - 4 - )] - pub plain_flag: bool, - - #[clap( - long = "debug", - help = "Output debug messages.", - global = true, - display_order = usize::MAX - 3 - )] - pub debug_flag: bool, - - #[clap( - long = "no-color", - help = "Disable output coloring.", - global = true, - display_order = usize::MAX - 2 - )] - pub no_color_flag: bool, - - #[clap( - long = "test", - help = "Avoid destructive modifications and show all output subject to the commandline filters. Useful for dry-runs and for developers.", - global = true, - display_order = usize::MAX - 1 - )] - pub test_flag: bool, - - #[clap(flatten)] - pub verbose: Verbosity, - } - - // region: IMPORTS - - use std::path::PathBuf; - - use clap::Args; - use clap_verbosity_flag::Verbosity; - - // endregion: IMPORTS -} - // endregion: MODULES // region: RE-EXPORTS -#[allow(unused_imports)] -pub use cli_template::*; - // endregion: RE-EXPORTS diff --git a/rust-toolchain.toml b/rust-toolchain.toml index af42841..175da5a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-03-17" +channel = "nightly" components = ["rustc", "cargo", "rust-std", "rustfmt", "clippy"] targets = [ "x86_64-unknown-linux-gnu",