flatten subcommands and add better doc comments
This commit is contained in:
parent
b0bffc45ee
commit
4dedde5edb
|
@ -32,10 +32,10 @@ You should now be able to connect to the mariadb instance, after building the pr
|
||||||
cargo run -- --config-file ./config.toml <args>
|
cargo run -- --config-file ./config.toml <args>
|
||||||
|
|
||||||
# example usage
|
# example usage
|
||||||
cargo run -- --config-file ./config.toml db create "${USER}_testdb"
|
cargo run -- --config-file ./config.toml create-db "${USER}_testdb"
|
||||||
cargo run -- --config-file ./config.toml user create "${USER}_testuser"
|
cargo run -- --config-file ./config.toml create-user "${USER}_testuser"
|
||||||
cargo run -- --config-file ./config.toml db edit-perm -p "${USER}_testdb:${USER}_testuser:A"
|
cargo run -- --config-file ./config.toml edit-db-perm -p "${USER}_testdb:${USER}_testuser:A"
|
||||||
cargo run -- --config-file ./config.toml db show-perm
|
cargo run -- --config-file ./config.toml show-db-perm
|
||||||
```
|
```
|
||||||
|
|
||||||
To stop and remove the container, run the following command:
|
To stop and remove the container, run the following command:
|
||||||
|
|
|
@ -15,40 +15,38 @@ use crate::core::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
pub struct DatabaseArgs {
|
|
||||||
#[clap(subcommand)]
|
|
||||||
subcmd: DatabaseCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Support batch creation/dropping,showing of databases,
|
// TODO: Support batch creation/dropping,showing of databases,
|
||||||
// using a comma-separated list of database names.
|
// using a comma-separated list of database names.
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
enum DatabaseCommand {
|
// #[command(next_help_heading = Some(DATABASE_COMMAND_HEADER))]
|
||||||
/// Create the DATABASE(S).
|
pub enum DatabaseCommand {
|
||||||
#[command(alias = "add", alias = "c")]
|
/// Create one or more databases
|
||||||
Create(DatabaseCreateArgs),
|
|
||||||
|
|
||||||
/// Delete the DATABASE(S).
|
|
||||||
#[command(alias = "remove", alias = "delete", alias = "rm", alias = "d")]
|
|
||||||
Drop(DatabaseDropArgs),
|
|
||||||
|
|
||||||
/// List the DATABASE(S) you own.
|
|
||||||
#[command()]
|
#[command()]
|
||||||
List(DatabaseListArgs),
|
CreateDb(DatabaseCreateArgs),
|
||||||
|
|
||||||
/// Give information about the DATABASE(S), or if none are given, all the ones you own.
|
/// Delete one or more databases
|
||||||
|
#[command()]
|
||||||
|
DropDb(DatabaseDropArgs),
|
||||||
|
|
||||||
|
/// List all databases you have access to
|
||||||
|
#[command()]
|
||||||
|
ListDb(DatabaseListArgs),
|
||||||
|
|
||||||
|
/// List user permissions for one or more databases
|
||||||
///
|
///
|
||||||
/// In particular, this will show the permissions for the database(s) owned by the current user.
|
/// If no database names are provided, it will show permissions for all databases you have access to.
|
||||||
#[command(alias = "s")]
|
#[command()]
|
||||||
ShowPerm(DatabaseShowPermArgs),
|
ShowDbPerm(DatabaseShowPermArgs),
|
||||||
|
|
||||||
/// Change permissions for the DATABASE(S). Run `edit-perm --help` for more information.
|
/// Change user permissions for one or more databases. See `edit-db-perm --help` for details.
|
||||||
///
|
///
|
||||||
/// This command has two modes of operation:
|
/// This command has two modes of operation:
|
||||||
|
///
|
||||||
/// 1. Interactive mode: If nothing else is specified, the user will be prompted to edit the permissions using a text editor.
|
/// 1. Interactive mode: If nothing else is specified, the user will be prompted to edit the permissions using a text editor.
|
||||||
///
|
///
|
||||||
|
/// You can configure your preferred text editor by setting the `VISUAL` or `EDITOR` environment variables.
|
||||||
|
///
|
||||||
/// Follow the instructions inside the editor for more information.
|
/// Follow the instructions inside the editor for more information.
|
||||||
///
|
///
|
||||||
/// 2. Non-interactive mode: If the `-p` flag is specified, the user can write permissions using arguments.
|
/// 2. Non-interactive mode: If the `-p` flag is specified, the user can write permissions using arguments.
|
||||||
|
@ -57,7 +55,7 @@ enum DatabaseCommand {
|
||||||
/// where the privileges are a string of characters, each representing a single permissions.
|
/// where the privileges are a string of characters, each representing a single permissions.
|
||||||
/// The character `A` is an exception, because it represents all permissions.
|
/// The character `A` is an exception, because it represents all permissions.
|
||||||
///
|
///
|
||||||
/// The permission to character mapping is as follows:
|
/// The character to permission mapping is declared as follows:
|
||||||
///
|
///
|
||||||
/// - `s` - SELECT
|
/// - `s` - SELECT
|
||||||
/// - `i` - INSERT
|
/// - `i` - INSERT
|
||||||
|
@ -72,33 +70,49 @@ enum DatabaseCommand {
|
||||||
/// - `r` - REFERENCES
|
/// - `r` - REFERENCES
|
||||||
/// - `A` - ALL PRIVILEGES
|
/// - `A` - ALL PRIVILEGES
|
||||||
///
|
///
|
||||||
#[command(display_name = "edit-perm", alias = "e", verbatim_doc_comment)]
|
/// If you provide a database name, you can omit it from the permission arguments.
|
||||||
EditPerm(DatabaseEditPermArgs),
|
///
|
||||||
|
/// Example usage of non-interactive mode:
|
||||||
|
///
|
||||||
|
/// Set permissions `SELECT`, `INSERT`, and `UPDATE` for user `my_user` on database `my_db`:
|
||||||
|
///
|
||||||
|
/// mysqladm edit-db-perm -p my_db:my_user:siu
|
||||||
|
///
|
||||||
|
/// Set all permissions for user `my_other_user` on database `my_other_db`:
|
||||||
|
///
|
||||||
|
/// mysqladm edit-db-perm -p my_other_db:my_other_user:A
|
||||||
|
///
|
||||||
|
/// Set miscellaneous permissions for multiple users on database `my_db`:
|
||||||
|
///
|
||||||
|
/// mysqladm edit-db-perm my_db -p my_user:siu my_other_user:ct
|
||||||
|
///
|
||||||
|
#[command(verbatim_doc_comment)]
|
||||||
|
EditDbPerm(DatabaseEditPermArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct DatabaseCreateArgs {
|
pub struct DatabaseCreateArgs {
|
||||||
/// The name of the database(s) to create.
|
/// The name of the database(s) to create.
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
name: Vec<String>,
|
name: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct DatabaseDropArgs {
|
pub struct DatabaseDropArgs {
|
||||||
/// The name of the database(s) to drop.
|
/// The name of the database(s) to drop.
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
name: Vec<String>,
|
name: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct DatabaseListArgs {
|
pub struct DatabaseListArgs {
|
||||||
/// Whether to output the information in JSON format.
|
/// Whether to output the information in JSON format.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct DatabaseShowPermArgs {
|
pub struct DatabaseShowPermArgs {
|
||||||
/// The name of the database(s) to show.
|
/// The name of the database(s) to show.
|
||||||
#[arg(num_args = 0..)]
|
#[arg(num_args = 0..)]
|
||||||
name: Vec<String>,
|
name: Vec<String>,
|
||||||
|
@ -109,7 +123,7 @@ struct DatabaseShowPermArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct DatabaseEditPermArgs {
|
pub struct DatabaseEditPermArgs {
|
||||||
/// The name of the database to edit permissions for.
|
/// The name of the database to edit permissions for.
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
|
@ -124,18 +138,21 @@ struct DatabaseEditPermArgs {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
editor: Option<String>,
|
editor: Option<String>,
|
||||||
|
|
||||||
/// Disable confirmation before saving changes.
|
/// Disable interactive confirmation before saving changes.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
yes: bool,
|
yes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_command(args: DatabaseArgs, mut conn: MySqlConnection) -> anyhow::Result<()> {
|
pub async fn handle_command(
|
||||||
let result = match args.subcmd {
|
command: DatabaseCommand,
|
||||||
DatabaseCommand::Create(args) => create_databases(args, &mut conn).await,
|
mut conn: MySqlConnection,
|
||||||
DatabaseCommand::Drop(args) => drop_databases(args, &mut conn).await,
|
) -> anyhow::Result<()> {
|
||||||
DatabaseCommand::List(args) => list_databases(args, &mut conn).await,
|
let result = match command {
|
||||||
DatabaseCommand::ShowPerm(args) => show_databases(args, &mut conn).await,
|
DatabaseCommand::CreateDb(args) => create_databases(args, &mut conn).await,
|
||||||
DatabaseCommand::EditPerm(args) => edit_permissions(args, &mut conn).await,
|
DatabaseCommand::DropDb(args) => drop_databases(args, &mut conn).await,
|
||||||
|
DatabaseCommand::ListDb(args) => list_databases(args, &mut conn).await,
|
||||||
|
DatabaseCommand::ShowDbPerm(args) => show_databases(args, &mut conn).await,
|
||||||
|
DatabaseCommand::EditDbPerm(args) => edit_permissions(args, &mut conn).await,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn.close().await?;
|
conn.close().await?;
|
||||||
|
|
|
@ -13,38 +13,40 @@ pub struct UserArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
enum UserCommand {
|
pub enum UserCommand {
|
||||||
/// Create the USER(s).
|
/// Create one or more users
|
||||||
#[command(alias = "add", alias = "c")]
|
#[command()]
|
||||||
Create(UserCreateArgs),
|
CreateUser(UserCreateArgs),
|
||||||
|
|
||||||
/// Delete the USER(s).
|
/// Delete one or more users
|
||||||
#[command(alias = "remove", alias = "delete", alias = "rm", alias = "d")]
|
#[command()]
|
||||||
Drop(UserDeleteArgs),
|
DropUser(UserDeleteArgs),
|
||||||
|
|
||||||
/// Change the MySQL password for the USER.
|
/// Change the MySQL password for a user
|
||||||
#[command(alias = "password", alias = "p")]
|
#[command()]
|
||||||
Passwd(UserPasswdArgs),
|
PasswdUser(UserPasswdArgs),
|
||||||
|
|
||||||
/// Give information about the USER(s), or if no USER is given, all USERs you have access to.
|
/// Give information about one or more users
|
||||||
#[command(alias = "list", alias = "ls", alias = "s")]
|
///
|
||||||
Show(UserShowArgs),
|
/// If no username is provided, all users you have access will be shown.
|
||||||
|
#[command()]
|
||||||
|
ShowUser(UserShowArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct UserCreateArgs {
|
pub struct UserCreateArgs {
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct UserDeleteArgs {
|
pub struct UserDeleteArgs {
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct UserPasswdArgs {
|
pub struct UserPasswdArgs {
|
||||||
username: String,
|
username: String,
|
||||||
|
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
|
@ -52,17 +54,17 @@ struct UserPasswdArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct UserShowArgs {
|
pub struct UserShowArgs {
|
||||||
#[arg(num_args = 0..)]
|
#[arg(num_args = 0..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_command(args: UserArgs, mut conn: MySqlConnection) -> anyhow::Result<()> {
|
pub async fn handle_command(command: UserCommand, mut conn: MySqlConnection) -> anyhow::Result<()> {
|
||||||
let result = match args.subcmd {
|
let result = match command {
|
||||||
UserCommand::Create(args) => create_users(args, &mut conn).await,
|
UserCommand::CreateUser(args) => create_users(args, &mut conn).await,
|
||||||
UserCommand::Drop(args) => drop_users(args, &mut conn).await,
|
UserCommand::DropUser(args) => drop_users(args, &mut conn).await,
|
||||||
UserCommand::Passwd(args) => change_password_for_user(args, &mut conn).await,
|
UserCommand::PasswdUser(args) => change_password_for_user(args, &mut conn).await,
|
||||||
UserCommand::Show(args) => show_users(args, &mut conn).await,
|
UserCommand::ShowUser(args) => show_users(args, &mut conn).await,
|
||||||
};
|
};
|
||||||
|
|
||||||
conn.close().await?;
|
conn.close().await?;
|
||||||
|
|
|
@ -20,60 +20,57 @@ pub struct MysqlConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct ConfigOverrideArgs {
|
pub struct GlobalConfigArgs {
|
||||||
|
/// Path to the configuration file.
|
||||||
#[arg(
|
#[arg(
|
||||||
|
short,
|
||||||
long,
|
long,
|
||||||
value_name = "PATH",
|
value_name = "PATH",
|
||||||
global = true,
|
global = true,
|
||||||
help_heading = Some("Configuration overrides"),
|
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
alias = "config",
|
default_value = "/etc/mysqladm/config.toml",
|
||||||
alias = "conf",
|
|
||||||
)]
|
)]
|
||||||
config_file: Option<String>,
|
config_file: String,
|
||||||
|
|
||||||
|
/// Hostname of the MySQL server.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "HOST",
|
value_name = "HOST",
|
||||||
global = true,
|
global = true,
|
||||||
help_heading = Some("Configuration overrides"),
|
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
)]
|
)]
|
||||||
mysql_host: Option<String>,
|
mysql_host: Option<String>,
|
||||||
|
|
||||||
|
/// Port of the MySQL server.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "PORT",
|
value_name = "PORT",
|
||||||
global = true,
|
global = true,
|
||||||
help_heading = Some("Configuration overrides"),
|
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
)]
|
)]
|
||||||
mysql_port: Option<u16>,
|
mysql_port: Option<u16>,
|
||||||
|
|
||||||
|
/// Username to use for the MySQL connection.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "USER",
|
value_name = "USER",
|
||||||
global = true,
|
global = true,
|
||||||
help_heading = Some("Configuration overrides"),
|
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
)]
|
)]
|
||||||
mysql_user: Option<String>,
|
mysql_user: Option<String>,
|
||||||
|
|
||||||
|
/// Path to a file containing the MySQL password.
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
value_name = "PATH",
|
value_name = "PATH",
|
||||||
global = true,
|
global = true,
|
||||||
help_heading = Some("Configuration overrides"),
|
|
||||||
hide_short_help = true,
|
hide_short_help = true,
|
||||||
)]
|
)]
|
||||||
mysql_password_file: Option<String>,
|
mysql_password_file: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(args: ConfigOverrideArgs) -> anyhow::Result<Config> {
|
pub fn get_config(args: GlobalConfigArgs) -> anyhow::Result<Config> {
|
||||||
let config_path = args
|
let config_path = PathBuf::from(args.config_file);
|
||||||
.config_file
|
|
||||||
.unwrap_or("/etc/mysqladm/config.toml".to_string());
|
|
||||||
let config_path = PathBuf::from(config_path);
|
|
||||||
|
|
||||||
let config: Config = fs::read_to_string(&config_path)
|
let config: Config = fs::read_to_string(&config_path)
|
||||||
.context(format!(
|
.context(format!(
|
||||||
|
@ -108,7 +105,7 @@ pub fn get_config(args: ConfigOverrideArgs) -> anyhow::Result<Config> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: Add timeout.
|
/// TODO: Make timeout configurable
|
||||||
pub async fn mysql_connection_from_config(config: Config) -> anyhow::Result<MySqlConnection> {
|
pub async fn mysql_connection_from_config(config: Config) -> anyhow::Result<MySqlConnection> {
|
||||||
match tokio::time::timeout(
|
match tokio::time::timeout(
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -15,28 +15,27 @@ struct Args {
|
||||||
command: Command,
|
command: Command,
|
||||||
|
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
config_overrides: core::config::ConfigOverrideArgs,
|
config_overrides: core::config::GlobalConfigArgs,
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
#[arg(short, long, alias = "tui", global = true)]
|
#[arg(short, long, alias = "tui", global = true)]
|
||||||
interactive: bool,
|
interactive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Database administration tool designed for non-admin users to manage their own MySQL databases and users.
|
/// Database administration tool for non-admin users to manage their own MySQL databases and users.
|
||||||
/// Use `--help` for advanced usage.
|
|
||||||
///
|
///
|
||||||
/// This tool allows you to manage users and databases in MySQL that are prefixed with your username.
|
/// This tool allows you to manage users and databases in MySQL.
|
||||||
|
///
|
||||||
|
/// You are only allowed to manage databases and users that are prefixed with
|
||||||
|
/// either your username, or a group that you are a member of.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version, about, disable_help_subcommand = true)]
|
#[command(version, about, disable_help_subcommand = true)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Create, drop or show/edit permissions for DATABASE(s),
|
#[command(flatten)]
|
||||||
#[command(alias = "database")]
|
Db(cli::database_command::DatabaseCommand),
|
||||||
Db(cli::database_command::DatabaseArgs),
|
|
||||||
|
|
||||||
// Database(cli::database_command::DatabaseArgs),
|
#[command(flatten)]
|
||||||
/// Create, drop, change password for, or show your USER(s),
|
User(cli::user_command::UserCommand),
|
||||||
#[command(name = "user")]
|
|
||||||
User(cli::user_command::UserArgs),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -47,9 +46,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let connection = core::config::mysql_connection_from_config(config).await?;
|
let connection = core::config::mysql_connection_from_config(config).await?;
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Command::Db(database_args) => {
|
Command::Db(command) => cli::database_command::handle_command(command, connection).await,
|
||||||
cli::database_command::handle_command(database_args, connection).await
|
|
||||||
}
|
|
||||||
Command::User(user_args) => cli::user_command::handle_command(user_args, connection).await,
|
Command::User(user_args) => cli::user_command::handle_command(user_args, connection).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue