diff --git a/src/client/commands.rs b/src/client/commands.rs index 77b9f8e..7f44f35 100644 --- a/src/client/commands.rs +++ b/src/client/commands.rs @@ -24,34 +24,30 @@ pub use show_privs::*; pub use show_user::*; pub use unlock_user::*; -use clap::Parser; +use clap::Subcommand; use crate::core::protocol::{ClientToServerMessageStream, Response}; -#[derive(Parser, Debug, Clone)] +#[derive(Subcommand, Debug, Clone)] +#[command(subcommand_required = true)] pub enum ClientCommand { /// Check whether you are authorized to manage the specified databases or users. - #[command()] CheckAuth(CheckAuthArgs), /// Create one or more databases - #[command()] CreateDb(CreateDbArgs), /// Delete one or more databases - #[command()] DropDb(DropDbArgs), /// Print information about one or more databases /// /// If no database name is provided, all databases you have access will be shown. - #[command()] ShowDb(ShowDbArgs), /// Print user privileges for one or more databases /// /// If no database names are provided, all databases you have access to will be shown. - #[command()] ShowPrivs(ShowPrivsArgs), /// Change user privileges for one or more databases. See `edit-privs --help` for details. @@ -116,29 +112,23 @@ pub enum ClientCommand { EditPrivs(EditPrivsArgs), /// Create one or more users - #[command()] CreateUser(CreateUserArgs), /// Delete one or more users - #[command()] DropUser(DropUserArgs), /// Change the MySQL password for a user - #[command()] PasswdUser(PasswdUserArgs), /// Print information about one or more users /// /// If no username is provided, all users you have access will be shown. - #[command()] ShowUser(ShowUserArgs), /// Lock account for one or more users - #[command()] LockUser(LockUserArgs), /// Unlock account for one or more users - #[command()] UnlockUser(UnlockUserArgs), } diff --git a/src/client/commands/check_auth.rs b/src/client/commands/check_auth.rs index 7729042..3d0caf9 100644 --- a/src/client/commands/check_auth.rs +++ b/src/client/commands/check_auth.rs @@ -15,7 +15,7 @@ use tokio_stream::StreamExt; #[derive(Parser, Debug, Clone)] pub struct CheckAuthArgs { /// The MySQL database(s) or user(s) to check authorization for - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "NAME")] name: Vec, /// Treat the provided names as users instead of databases diff --git a/src/client/commands/create_db.rs b/src/client/commands/create_db.rs index 9b64b20..91d7294 100644 --- a/src/client/commands/create_db.rs +++ b/src/client/commands/create_db.rs @@ -16,7 +16,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct CreateDbArgs { /// The MySQL database(s) to create - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "DB_NAME")] name: Vec, /// Print the information as JSON diff --git a/src/client/commands/create_user.rs b/src/client/commands/create_user.rs index 2883891..613b913 100644 --- a/src/client/commands/create_user.rs +++ b/src/client/commands/create_user.rs @@ -17,7 +17,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct CreateUserArgs { /// The MySQL user(s) to create - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "USER_NAME")] username: Vec, /// Do not ask for a password, leave it unset diff --git a/src/client/commands/drop_db.rs b/src/client/commands/drop_db.rs index f03fe43..a80abfe 100644 --- a/src/client/commands/drop_db.rs +++ b/src/client/commands/drop_db.rs @@ -19,7 +19,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct DropDbArgs { /// The MySQL database(s) to drop - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "DB_NAME")] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_database_completer)))] name: Vec, diff --git a/src/client/commands/drop_user.rs b/src/client/commands/drop_user.rs index 5c9adb1..fdb8d4d 100644 --- a/src/client/commands/drop_user.rs +++ b/src/client/commands/drop_user.rs @@ -19,7 +19,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct DropUserArgs { /// The MySQL user(s) to drop - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "USER_NAME")] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_user_completer)))] username: Vec, diff --git a/src/client/commands/edit_privs.rs b/src/client/commands/edit_privs.rs index 46d1020..71ce15a 100644 --- a/src/client/commands/edit_privs.rs +++ b/src/client/commands/edit_privs.rs @@ -30,6 +30,7 @@ use crate::{ pub struct EditPrivsArgs { /// The MySQL database to edit privileges for #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_database_completer)))] + #[arg(value_name = "DB_NAME")] pub name: Option, #[arg( diff --git a/src/client/commands/lock_user.rs b/src/client/commands/lock_user.rs index 5490427..4d663f6 100644 --- a/src/client/commands/lock_user.rs +++ b/src/client/commands/lock_user.rs @@ -18,7 +18,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct LockUserArgs { /// The MySQL user(s) to loc - #[arg(num_args = 1..)] + #[arg(num_args = 1.., value_name = "USER_NAME")] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_user_completer)))] username: Vec, diff --git a/src/client/commands/passwd_user.rs b/src/client/commands/passwd_user.rs index 1029240..1144df9 100644 --- a/src/client/commands/passwd_user.rs +++ b/src/client/commands/passwd_user.rs @@ -23,6 +23,7 @@ use crate::{ pub struct PasswdUserArgs { /// The MySQL user whose password is to be changed #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_user_completer)))] + #[arg(value_name = "USER_NAME")] username: MySQLUser, /// Read the new password from a file instead of prompting for it diff --git a/src/client/commands/show_db.rs b/src/client/commands/show_db.rs index cc16cdb..3405c40 100644 --- a/src/client/commands/show_db.rs +++ b/src/client/commands/show_db.rs @@ -18,7 +18,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct ShowDbArgs { /// The MySQL database(s) to show - #[arg(num_args = 0..)] + #[arg(num_args = 0.., value_name = "DB_NAME")] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_database_completer)))] name: Vec, diff --git a/src/client/commands/show_privs.rs b/src/client/commands/show_privs.rs index b9fd1bb..89dcd2c 100644 --- a/src/client/commands/show_privs.rs +++ b/src/client/commands/show_privs.rs @@ -19,7 +19,7 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct ShowPrivsArgs { /// The MySQL database(s) to show privileges for - #[arg(num_args = 0..)] + #[arg(num_args = 0.., value_name = "DB_NAME")] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_database_completer)))] name: Vec, diff --git a/src/client/commands/show_user.rs b/src/client/commands/show_user.rs index c4fcdba..4a5656e 100644 --- a/src/client/commands/show_user.rs +++ b/src/client/commands/show_user.rs @@ -18,8 +18,8 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct ShowUserArgs { /// The MySQL user(s) to show - #[arg(num_args = 0..)] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_user_completer)))] + #[arg(num_args = 0.., value_name = "USER_NAME")] username: Vec, /// Print the information as JSON diff --git a/src/client/commands/unlock_user.rs b/src/client/commands/unlock_user.rs index b509bcd..0be189c 100644 --- a/src/client/commands/unlock_user.rs +++ b/src/client/commands/unlock_user.rs @@ -18,8 +18,8 @@ use crate::{ #[derive(Parser, Debug, Clone)] pub struct UnlockUserArgs { /// The MySQL user(s) to unlock - #[arg(num_args = 1..)] #[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(mysql_user_completer)))] + #[arg(num_args = 1.., value_name = "USER_NAME")] username: Vec, /// Print the information as JSON diff --git a/src/client/mysql_admutils_compatibility/mysql_dbadm.rs b/src/client/mysql_admutils_compatibility/mysql_dbadm.rs index be5df62..63c81fa 100644 --- a/src/client/mysql_admutils_compatibility/mysql_dbadm.rs +++ b/src/client/mysql_admutils_compatibility/mysql_dbadm.rs @@ -1,4 +1,4 @@ -use clap::Parser; +use clap::{Parser, Subcommand}; use clap_complete::ArgValueCompleter; use futures_util::{SinkExt, StreamExt}; use std::os::unix::net::UnixStream as StdUnixStream; @@ -98,7 +98,7 @@ pub struct Args { // NOTE: mysql-dbadm explicitly calls privileges "permissions". // This is something we're trying to move away from. // See https://git.pvv.ntnu.no/Projects/muscl/issues/29 -#[derive(Parser)] +#[derive(Subcommand)] pub enum Command { /// create the DATABASE(s). Create(CreateArgs), diff --git a/src/client/mysql_admutils_compatibility/mysql_useradm.rs b/src/client/mysql_admutils_compatibility/mysql_useradm.rs index 74a52ab..c0fa4bf 100644 --- a/src/client/mysql_admutils_compatibility/mysql_useradm.rs +++ b/src/client/mysql_admutils_compatibility/mysql_useradm.rs @@ -1,4 +1,4 @@ -use clap::Parser; +use clap::{Parser, Subcommand}; use clap_complete::ArgValueCompleter; use futures_util::{SinkExt, StreamExt}; use std::path::PathBuf; @@ -67,7 +67,7 @@ pub struct Args { config: Option, } -#[derive(Parser)] +#[derive(Subcommand)] pub enum Command { /// create the USER(s). Create(CreateArgs), diff --git a/src/main.rs b/src/main.rs index 0b8d42b..60aaaf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,9 +75,12 @@ const LONG_VERSION: &str = long_version(); version, about, disable_help_subcommand = true, + propagate_version = true, before_long_help = ASCII_BANNER, after_long_help = KIND_REGARDS, long_version = LONG_VERSION, + // NOTE: All non-registered "subcommands" are processed before Arg::parse() is called. + subcommand_required = true, )] struct Args { #[command(subcommand)] @@ -169,8 +172,8 @@ fn handle_dynamic_completion() -> anyhow::Result> { let command = match argv0.as_str() { "muscl" => Args::command(), - "mysql-dbadm" => mysql_dbadm::Command::command(), - "mysql-useradm" => mysql_useradm::Command::command(), + "mysql-dbadm" => mysql_dbadm::Args::command(), + "mysql-useradm" => mysql_useradm::Args::command(), command => anyhow::bail!("Unknown executable name: `{}`", command), }; diff --git a/src/server/command.rs b/src/server/command.rs index 6f1d07f..f624e9e 100644 --- a/src/server/command.rs +++ b/src/server/command.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use anyhow::Context; -use clap::Parser; +use clap::{Parser, Subcommand}; use clap_verbosity_flag::{InfoLevel, Verbosity}; use tracing_subscriber::prelude::*; @@ -26,15 +26,13 @@ pub struct ServerArgs { pub disable_landlock: bool, } -#[derive(Parser, Debug, Clone)] +#[derive(Subcommand, Debug, Clone)] pub enum ServerCommand { /// Start the server and listen for incoming connections on the unix socket /// specified in the configuration file. - #[command()] Listen, /// Start the server using systemd socket activation. - #[command()] SocketActivate, }