diff --git a/src/cli/common.rs b/src/cli/common.rs deleted file mode 100644 index 02087b1..0000000 --- a/src/cli/common.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::core::protocol::Response; - -pub fn erroneous_server_response( - response: Option>, -) -> anyhow::Result<()> { - match response { - Some(Ok(Response::Error(e))) => { - anyhow::bail!("Server returned error: {}", e); - } - Some(Err(e)) => { - anyhow::bail!(e); - } - Some(response) => { - anyhow::bail!("Unexpected response from server: {:?}", response); - } - None => { - anyhow::bail!("No response from server"); - } - } -} diff --git a/src/cli/user_command.rs b/src/cli/user_command.rs deleted file mode 100644 index a2a6f97..0000000 --- a/src/cli/user_command.rs +++ /dev/null @@ -1,425 +0,0 @@ -use anyhow::Context; -use clap::Parser; -use dialoguer::{Confirm, Password}; -use futures_util::{SinkExt, StreamExt}; - -use crate::core::protocol::{ - ClientToServerMessageStream, ListUsersError, MySQLUser, Request, Response, - print_create_users_output_status, print_create_users_output_status_json, - print_drop_users_output_status, print_drop_users_output_status_json, - print_lock_users_output_status, print_lock_users_output_status_json, - print_set_password_output_status, print_unlock_users_output_status, - print_unlock_users_output_status_json, -}; - -use super::common::erroneous_server_response; - -#[derive(Parser, Debug, Clone)] -pub struct UserArgs { - #[clap(subcommand)] - subcmd: UserCommand, -} - -#[allow(clippy::enum_variant_names)] -#[derive(Parser, Debug, Clone)] -pub enum UserCommand { - /// Create one or more users - #[command()] - CreateUser(UserCreateArgs), - - /// Delete one or more users - #[command()] - DropUser(UserDeleteArgs), - - /// Change the MySQL password for a user - #[command()] - PasswdUser(UserPasswdArgs), - - /// Print information about one or more users - /// - /// If no username is provided, all users you have access will be shown. - #[command()] - ShowUser(UserShowArgs), - - /// Lock account for one or more users - #[command()] - LockUser(UserLockArgs), - - /// Unlock account for one or more users - #[command()] - UnlockUser(UserUnlockArgs), -} - -#[derive(Parser, Debug, Clone)] -pub struct UserCreateArgs { - #[arg(num_args = 1..)] - username: Vec, - - /// Do not ask for a password, leave it unset - #[clap(long)] - no_password: bool, - - /// Print the information as JSON - /// - /// Note that this implies `--no-password`, since the command will become non-interactive. - #[arg(short, long)] - json: bool, -} - -#[derive(Parser, Debug, Clone)] -pub struct UserDeleteArgs { - #[arg(num_args = 1..)] - username: Vec, - - /// Print the information as JSON - #[arg(short, long)] - json: bool, -} - -#[derive(Parser, Debug, Clone)] -pub struct UserPasswdArgs { - username: MySQLUser, - - #[clap(short, long)] - password_file: Option, - - /// Print the information as JSON - #[arg(short, long)] - json: bool, -} - -#[derive(Parser, Debug, Clone)] -pub struct UserShowArgs { - #[arg(num_args = 0..)] - username: Vec, - - /// Print the information as JSON - #[arg(short, long)] - json: bool, -} - -#[derive(Parser, Debug, Clone)] -pub struct UserLockArgs { - #[arg(num_args = 1..)] - username: Vec, - - /// Print the information as JSON - #[arg(short, long)] - json: bool, -} - -#[derive(Parser, Debug, Clone)] -pub struct UserUnlockArgs { - #[arg(num_args = 1..)] - username: Vec, - - /// Print the information as JSON - #[arg(short, long)] - json: bool, -} - -pub async fn handle_command( - command: UserCommand, - server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - match command { - UserCommand::CreateUser(args) => create_users(args, server_connection).await, - UserCommand::DropUser(args) => drop_users(args, server_connection).await, - UserCommand::PasswdUser(args) => passwd_user(args, server_connection).await, - UserCommand::ShowUser(args) => show_users(args, server_connection).await, - UserCommand::LockUser(args) => lock_users(args, server_connection).await, - UserCommand::UnlockUser(args) => unlock_users(args, server_connection).await, - } -} - -async fn create_users( - args: UserCreateArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - if args.username.is_empty() { - anyhow::bail!("No usernames provided"); - } - - let message = Request::CreateUsers(args.username.to_owned()); - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(anyhow::Error::from(err).context("Failed to communicate with server")); - } - - let result = match server_connection.next().await { - Some(Ok(Response::CreateUsers(result))) => result, - response => return erroneous_server_response(response), - }; - - if args.json { - print_create_users_output_status_json(&result); - } else { - print_create_users_output_status(&result); - - let successfully_created_users = result - .iter() - .filter_map(|(username, result)| result.as_ref().ok().map(|_| username)) - .collect::>(); - - for username in successfully_created_users { - if !args.no_password - && Confirm::new() - .with_prompt(format!( - "Do you want to set a password for user '{}'?", - username - )) - .default(false) - .interact()? - { - let password = read_password_from_stdin_with_double_check(username)?; - let message = Request::PasswdUser(username.to_owned(), password); - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - match server_connection.next().await { - Some(Ok(Response::PasswdUser(result))) => { - print_set_password_output_status(&result, username) - } - response => return erroneous_server_response(response), - } - - println!(); - } - } - } - - server_connection.send(Request::Exit).await?; - - Ok(()) -} - -async fn drop_users( - args: UserDeleteArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - if args.username.is_empty() { - anyhow::bail!("No usernames provided"); - } - - let message = Request::DropUsers(args.username.to_owned()); - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - let result = match server_connection.next().await { - Some(Ok(Response::DropUsers(result))) => result, - response => return erroneous_server_response(response), - }; - - server_connection.send(Request::Exit).await?; - - if args.json { - print_drop_users_output_status_json(&result); - } else { - print_drop_users_output_status(&result); - } - - Ok(()) -} - -pub fn read_password_from_stdin_with_double_check(username: &MySQLUser) -> anyhow::Result { - Password::new() - .with_prompt(format!("New MySQL password for user '{}'", username)) - .with_confirmation( - format!("Retype new MySQL password for user '{}'", username), - "Passwords do not match", - ) - .interact() - .map_err(Into::into) -} - -async fn passwd_user( - args: UserPasswdArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - // TODO: create a "user" exists check" command - let message = Request::ListUsers(Some(vec![args.username.to_owned()])); - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - let response = match server_connection.next().await { - Some(Ok(Response::ListUsers(users))) => users, - response => return erroneous_server_response(response), - }; - match response - .get(&args.username) - .unwrap_or(&Err(ListUsersError::UserDoesNotExist)) - { - Ok(_) => {} - Err(err) => { - server_connection.send(Request::Exit).await?; - server_connection.close().await.ok(); - anyhow::bail!("{}", err.to_error_message(&args.username)); - } - } - - let password = if let Some(password_file) = args.password_file { - std::fs::read_to_string(password_file) - .context("Failed to read password file")? - .trim() - .to_string() - } else { - read_password_from_stdin_with_double_check(&args.username)? - }; - - let message = Request::PasswdUser(args.username.to_owned(), password); - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - let result = match server_connection.next().await { - Some(Ok(Response::PasswdUser(result))) => result, - response => return erroneous_server_response(response), - }; - - server_connection.send(Request::Exit).await?; - - print_set_password_output_status(&result, &args.username); - - Ok(()) -} - -async fn show_users( - args: UserShowArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - let message = if args.username.is_empty() { - Request::ListUsers(None) - } else { - Request::ListUsers(Some(args.username.to_owned())) - }; - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - let users = match server_connection.next().await { - Some(Ok(Response::ListUsers(users))) => users - .into_iter() - .filter_map(|(username, result)| match result { - Ok(user) => Some(user), - Err(err) => { - eprintln!("{}", err.to_error_message(&username)); - eprintln!("Skipping..."); - None - } - }) - .collect::>(), - Some(Ok(Response::ListAllUsers(users))) => match users { - Ok(users) => users, - Err(err) => { - server_connection.send(Request::Exit).await?; - return Err( - anyhow::anyhow!(err.to_error_message()).context("Failed to list all users") - ); - } - }, - response => return erroneous_server_response(response), - }; - - server_connection.send(Request::Exit).await?; - - if args.json { - println!( - "{}", - serde_json::to_string_pretty(&users).context("Failed to serialize users to JSON")? - ); - } else if users.is_empty() { - println!("No users to show."); - } else { - let mut table = prettytable::Table::new(); - table.add_row(row![ - "User", - "Password is set", - "Locked", - "Databases where user has privileges" - ]); - for user in users { - table.add_row(row![ - user.user, - user.has_password, - user.is_locked, - user.databases.join("\n") - ]); - } - table.printstd(); - } - - Ok(()) -} - -async fn lock_users( - args: UserLockArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - if args.username.is_empty() { - anyhow::bail!("No usernames provided"); - } - - let message = Request::LockUsers(args.username.to_owned()); - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - let result = match server_connection.next().await { - Some(Ok(Response::LockUsers(result))) => result, - response => return erroneous_server_response(response), - }; - - server_connection.send(Request::Exit).await?; - - if args.json { - print_lock_users_output_status_json(&result); - } else { - print_lock_users_output_status(&result); - } - - Ok(()) -} - -async fn unlock_users( - args: UserUnlockArgs, - mut server_connection: ClientToServerMessageStream, -) -> anyhow::Result<()> { - if args.username.is_empty() { - anyhow::bail!("No usernames provided"); - } - - let message = Request::UnlockUsers(args.username.to_owned()); - - if let Err(err) = server_connection.send(message).await { - server_connection.close().await.ok(); - anyhow::bail!(err); - } - - let result = match server_connection.next().await { - Some(Ok(Response::UnlockUsers(result))) => result, - response => return erroneous_server_response(response), - }; - - server_connection.send(Request::Exit).await?; - - if args.json { - print_unlock_users_output_status_json(&result); - } else { - print_unlock_users_output_status(&result); - } - - Ok(()) -} diff --git a/src/cli.rs b/src/client.rs similarity index 59% rename from src/cli.rs rename to src/client.rs index 1b29138..6ad1537 100644 --- a/src/cli.rs +++ b/src/client.rs @@ -1,6 +1,4 @@ -mod common; -pub mod database_command; -pub mod user_command; +pub mod command; #[cfg(feature = "mysql-admutils-compatibility")] pub mod mysql_admutils_compatibility; diff --git a/src/cli/database_command.rs b/src/client/command.rs similarity index 54% rename from src/cli/database_command.rs rename to src/client/command.rs index 4fdcdc9..4788f26 100644 --- a/src/cli/database_command.rs +++ b/src/client/command.rs @@ -2,13 +2,12 @@ use std::collections::BTreeSet; use anyhow::Context; use clap::Parser; -use dialoguer::{Confirm, Editor}; +use dialoguer::{Confirm, Editor, Password}; use futures_util::{SinkExt, StreamExt}; use nix::unistd::{User, getuid}; use prettytable::{Cell, Row, Table}; use crate::{ - cli::common::erroneous_server_response, core::{ common::yn, database_privileges::{ @@ -19,17 +18,21 @@ use crate::{ reduce_privilege_diffs, }, protocol::{ - ClientToServerMessageStream, MySQLDatabase, Request, Response, - print_create_databases_output_status, print_create_databases_output_status_json, - print_drop_databases_output_status, print_drop_databases_output_status_json, - print_modify_database_privileges_output_status, + ClientToServerMessageStream, ListUsersError, MySQLDatabase, MySQLUser, Request, + Response, print_create_databases_output_status, + print_create_databases_output_status_json, print_create_users_output_status, + print_create_users_output_status_json, print_drop_databases_output_status, + print_drop_databases_output_status_json, print_drop_users_output_status, + print_drop_users_output_status_json, print_lock_users_output_status, + print_lock_users_output_status_json, print_modify_database_privileges_output_status, + print_set_password_output_status, print_unlock_users_output_status, + print_unlock_users_output_status_json, }, }, }; #[derive(Parser, Debug, Clone)] -// #[command(next_help_heading = Some(DATABASE_COMMAND_HEADER))] -pub enum DatabaseCommand { +pub enum ClientCommand { /// Create one or more databases #[command()] CreateDb(DatabaseCreateArgs), @@ -102,6 +105,32 @@ pub enum DatabaseCommand { /// #[command(verbatim_doc_comment)] EditDbPrivs(DatabaseEditPrivsArgs), + + /// Create one or more users + #[command()] + CreateUser(UserCreateArgs), + + /// Delete one or more users + #[command()] + DropUser(UserDeleteArgs), + + /// Change the MySQL password for a user + #[command()] + PasswdUser(UserPasswdArgs), + + /// Print information about one or more users + /// + /// If no username is provided, all users you have access will be shown. + #[command()] + ShowUser(UserShowArgs), + + /// Lock account for one or more users + #[command()] + LockUser(UserLockArgs), + + /// Unlock account for one or more users + #[command()] + UnlockUser(UserUnlockArgs), } #[derive(Parser, Debug, Clone)] @@ -169,19 +198,108 @@ pub struct DatabaseEditPrivsArgs { pub yes: bool, } +#[derive(Parser, Debug, Clone)] +pub struct UserCreateArgs { + #[arg(num_args = 1..)] + username: Vec, + + /// Do not ask for a password, leave it unset + #[clap(long)] + no_password: bool, + + /// Print the information as JSON + /// + /// Note that this implies `--no-password`, since the command will become non-interactive. + #[arg(short, long)] + json: bool, +} + +#[derive(Parser, Debug, Clone)] +pub struct UserDeleteArgs { + #[arg(num_args = 1..)] + username: Vec, + + /// Print the information as JSON + #[arg(short, long)] + json: bool, +} + +#[derive(Parser, Debug, Clone)] +pub struct UserPasswdArgs { + username: MySQLUser, + + #[clap(short, long)] + password_file: Option, + + /// Print the information as JSON + #[arg(short, long)] + json: bool, +} + +#[derive(Parser, Debug, Clone)] +pub struct UserShowArgs { + #[arg(num_args = 0..)] + username: Vec, + + /// Print the information as JSON + #[arg(short, long)] + json: bool, +} + +#[derive(Parser, Debug, Clone)] +pub struct UserLockArgs { + #[arg(num_args = 1..)] + username: Vec, + + /// Print the information as JSON + #[arg(short, long)] + json: bool, +} + +#[derive(Parser, Debug, Clone)] +pub struct UserUnlockArgs { + #[arg(num_args = 1..)] + username: Vec, + + /// Print the information as JSON + #[arg(short, long)] + json: bool, +} + pub async fn handle_command( - command: DatabaseCommand, + command: ClientCommand, server_connection: ClientToServerMessageStream, ) -> anyhow::Result<()> { match command { - DatabaseCommand::CreateDb(args) => create_databases(args, server_connection).await, - DatabaseCommand::DropDb(args) => drop_databases(args, server_connection).await, - DatabaseCommand::ShowDb(args) => show_databases(args, server_connection).await, - DatabaseCommand::ShowDbPrivs(args) => { - show_database_privileges(args, server_connection).await + ClientCommand::CreateDb(args) => create_databases(args, server_connection).await, + ClientCommand::DropDb(args) => drop_databases(args, server_connection).await, + ClientCommand::ShowDb(args) => show_databases(args, server_connection).await, + ClientCommand::ShowDbPrivs(args) => show_database_privileges(args, server_connection).await, + ClientCommand::EditDbPrivs(args) => edit_database_privileges(args, server_connection).await, + ClientCommand::CreateUser(args) => create_users(args, server_connection).await, + ClientCommand::DropUser(args) => drop_users(args, server_connection).await, + ClientCommand::PasswdUser(args) => passwd_user(args, server_connection).await, + ClientCommand::ShowUser(args) => show_users(args, server_connection).await, + ClientCommand::LockUser(args) => lock_users(args, server_connection).await, + ClientCommand::UnlockUser(args) => unlock_users(args, server_connection).await, + } +} + +pub fn erroneous_server_response( + response: Option>, +) -> anyhow::Result<()> { + match response { + Some(Ok(Response::Error(e))) => { + anyhow::bail!("Server returned error: {}", e); } - DatabaseCommand::EditDbPrivs(args) => { - edit_database_privileges(args, server_connection).await + Some(Err(e)) => { + anyhow::bail!(e); + } + Some(response) => { + anyhow::bail!("Unexpected response from server: {:?}", response); + } + None => { + anyhow::bail!("No response from server"); } } } @@ -487,3 +605,295 @@ fn edit_privileges_with_editor( .context("Could not parse privilege data from editor"), } } + +async fn create_users( + args: UserCreateArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + let message = Request::CreateUsers(args.username.to_owned()); + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(anyhow::Error::from(err).context("Failed to communicate with server")); + } + + let result = match server_connection.next().await { + Some(Ok(Response::CreateUsers(result))) => result, + response => return erroneous_server_response(response), + }; + + if args.json { + print_create_users_output_status_json(&result); + } else { + print_create_users_output_status(&result); + + let successfully_created_users = result + .iter() + .filter_map(|(username, result)| result.as_ref().ok().map(|_| username)) + .collect::>(); + + for username in successfully_created_users { + if !args.no_password + && Confirm::new() + .with_prompt(format!( + "Do you want to set a password for user '{}'?", + username + )) + .default(false) + .interact()? + { + let password = read_password_from_stdin_with_double_check(username)?; + let message = Request::PasswdUser(username.to_owned(), password); + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + match server_connection.next().await { + Some(Ok(Response::PasswdUser(result))) => { + print_set_password_output_status(&result, username) + } + response => return erroneous_server_response(response), + } + + println!(); + } + } + } + + server_connection.send(Request::Exit).await?; + + Ok(()) +} + +async fn drop_users( + args: UserDeleteArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + let message = Request::DropUsers(args.username.to_owned()); + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + let result = match server_connection.next().await { + Some(Ok(Response::DropUsers(result))) => result, + response => return erroneous_server_response(response), + }; + + server_connection.send(Request::Exit).await?; + + if args.json { + print_drop_users_output_status_json(&result); + } else { + print_drop_users_output_status(&result); + } + + Ok(()) +} + +pub fn read_password_from_stdin_with_double_check(username: &MySQLUser) -> anyhow::Result { + Password::new() + .with_prompt(format!("New MySQL password for user '{}'", username)) + .with_confirmation( + format!("Retype new MySQL password for user '{}'", username), + "Passwords do not match", + ) + .interact() + .map_err(Into::into) +} + +async fn passwd_user( + args: UserPasswdArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + // TODO: create a "user" exists check" command + let message = Request::ListUsers(Some(vec![args.username.to_owned()])); + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + let response = match server_connection.next().await { + Some(Ok(Response::ListUsers(users))) => users, + response => return erroneous_server_response(response), + }; + match response + .get(&args.username) + .unwrap_or(&Err(ListUsersError::UserDoesNotExist)) + { + Ok(_) => {} + Err(err) => { + server_connection.send(Request::Exit).await?; + server_connection.close().await.ok(); + anyhow::bail!("{}", err.to_error_message(&args.username)); + } + } + + let password = if let Some(password_file) = args.password_file { + std::fs::read_to_string(password_file) + .context("Failed to read password file")? + .trim() + .to_string() + } else { + read_password_from_stdin_with_double_check(&args.username)? + }; + + let message = Request::PasswdUser(args.username.to_owned(), password); + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + let result = match server_connection.next().await { + Some(Ok(Response::PasswdUser(result))) => result, + response => return erroneous_server_response(response), + }; + + server_connection.send(Request::Exit).await?; + + print_set_password_output_status(&result, &args.username); + + Ok(()) +} + +async fn show_users( + args: UserShowArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + let message = if args.username.is_empty() { + Request::ListUsers(None) + } else { + Request::ListUsers(Some(args.username.to_owned())) + }; + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + let users = match server_connection.next().await { + Some(Ok(Response::ListUsers(users))) => users + .into_iter() + .filter_map(|(username, result)| match result { + Ok(user) => Some(user), + Err(err) => { + eprintln!("{}", err.to_error_message(&username)); + eprintln!("Skipping..."); + None + } + }) + .collect::>(), + Some(Ok(Response::ListAllUsers(users))) => match users { + Ok(users) => users, + Err(err) => { + server_connection.send(Request::Exit).await?; + return Err( + anyhow::anyhow!(err.to_error_message()).context("Failed to list all users") + ); + } + }, + response => return erroneous_server_response(response), + }; + + server_connection.send(Request::Exit).await?; + + if args.json { + println!( + "{}", + serde_json::to_string_pretty(&users).context("Failed to serialize users to JSON")? + ); + } else if users.is_empty() { + println!("No users to show."); + } else { + let mut table = prettytable::Table::new(); + table.add_row(row![ + "User", + "Password is set", + "Locked", + "Databases where user has privileges" + ]); + for user in users { + table.add_row(row![ + user.user, + user.has_password, + user.is_locked, + user.databases.join("\n") + ]); + } + table.printstd(); + } + + Ok(()) +} + +async fn lock_users( + args: UserLockArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + let message = Request::LockUsers(args.username.to_owned()); + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + let result = match server_connection.next().await { + Some(Ok(Response::LockUsers(result))) => result, + response => return erroneous_server_response(response), + }; + + server_connection.send(Request::Exit).await?; + + if args.json { + print_lock_users_output_status_json(&result); + } else { + print_lock_users_output_status(&result); + } + + Ok(()) +} + +async fn unlock_users( + args: UserUnlockArgs, + mut server_connection: ClientToServerMessageStream, +) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + let message = Request::UnlockUsers(args.username.to_owned()); + + if let Err(err) = server_connection.send(message).await { + server_connection.close().await.ok(); + anyhow::bail!(err); + } + + let result = match server_connection.next().await { + Some(Ok(Response::UnlockUsers(result))) => result, + response => return erroneous_server_response(response), + }; + + server_connection.send(Request::Exit).await?; + + if args.json { + print_unlock_users_output_status_json(&result); + } else { + print_unlock_users_output_status(&result); + } + + Ok(()) +} diff --git a/src/cli/mysql_admutils_compatibility.rs b/src/client/mysql_admutils_compatibility.rs similarity index 100% rename from src/cli/mysql_admutils_compatibility.rs rename to src/client/mysql_admutils_compatibility.rs diff --git a/src/cli/mysql_admutils_compatibility/common.rs b/src/client/mysql_admutils_compatibility/common.rs similarity index 100% rename from src/cli/mysql_admutils_compatibility/common.rs rename to src/client/mysql_admutils_compatibility/common.rs diff --git a/src/cli/mysql_admutils_compatibility/error_messages.rs b/src/client/mysql_admutils_compatibility/error_messages.rs similarity index 100% rename from src/cli/mysql_admutils_compatibility/error_messages.rs rename to src/client/mysql_admutils_compatibility/error_messages.rs diff --git a/src/cli/mysql_admutils_compatibility/mysql_dbadm.rs b/src/client/mysql_admutils_compatibility/mysql_dbadm.rs similarity index 97% rename from src/cli/mysql_admutils_compatibility/mysql_dbadm.rs rename to src/client/mysql_admutils_compatibility/mysql_dbadm.rs index c2d86db..b235504 100644 --- a/src/cli/mysql_admutils_compatibility/mysql_dbadm.rs +++ b/src/client/mysql_admutils_compatibility/mysql_dbadm.rs @@ -5,9 +5,8 @@ use std::path::PathBuf; use tokio::net::UnixStream as TokioUnixStream; use crate::{ - cli::{ - common::erroneous_server_response, - database_command, + client::{ + command::{DatabaseEditPrivsArgs, edit_database_privileges, erroneous_server_response}, mysql_admutils_compatibility::{ common::trim_db_name_to_32_chars, error_messages::{ @@ -187,7 +186,7 @@ fn tokio_run_command(command: Command, server_connection: StdUnixStream) -> anyh Command::Drop(args) => drop_databases(args, message_stream).await, Command::Show(args) => show_databases(args, message_stream).await, Command::Editperm(args) => { - let edit_privileges_args = database_command::DatabaseEditPrivsArgs { + let edit_privileges_args = DatabaseEditPrivsArgs { name: Some(args.database), privs: vec![], json: false, @@ -195,8 +194,7 @@ fn tokio_run_command(command: Command, server_connection: StdUnixStream) -> anyh yes: false, }; - database_command::edit_database_privileges(edit_privileges_args, message_stream) - .await + edit_database_privileges(edit_privileges_args, message_stream).await } } }) diff --git a/src/cli/mysql_admutils_compatibility/mysql_useradm.rs b/src/client/mysql_admutils_compatibility/mysql_useradm.rs similarity index 98% rename from src/cli/mysql_admutils_compatibility/mysql_useradm.rs rename to src/client/mysql_admutils_compatibility/mysql_useradm.rs index b519553..471c902 100644 --- a/src/cli/mysql_admutils_compatibility/mysql_useradm.rs +++ b/src/client/mysql_admutils_compatibility/mysql_useradm.rs @@ -6,15 +6,13 @@ use std::os::unix::net::UnixStream as StdUnixStream; use tokio::net::UnixStream as TokioUnixStream; use crate::{ - cli::{ - common::erroneous_server_response, - mysql_admutils_compatibility::{ + client::{ + command::{erroneous_server_response, read_password_from_stdin_with_double_check}, mysql_admutils_compatibility::{ common::trim_user_name_to_32_chars, error_messages::{ handle_create_user_error, handle_drop_user_error, handle_list_users_error, }, - }, - user_command::read_password_from_stdin_with_double_check, + } }, core::{ bootstrap::bootstrap_server_connection_and_drop_privileges, diff --git a/src/main.rs b/src/main.rs index fd9c12c..efa337e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,11 +23,11 @@ use crate::{ }; #[cfg(feature = "mysql-admutils-compatibility")] -use crate::cli::mysql_admutils_compatibility::{mysql_dbadm, mysql_useradm}; +use crate::client::mysql_admutils_compatibility::{mysql_dbadm, mysql_useradm}; mod server; -mod cli; +mod client; mod core; #[cfg(feature = "tui")] @@ -77,10 +77,7 @@ struct Args { #[derive(Parser, Debug, Clone)] enum Command { #[command(flatten)] - Db(cli::database_command::DatabaseCommand), - - #[command(flatten)] - User(cli::user_command::UserCommand), + Client(client::command::ClientCommand), #[command(hide = true)] Server(server::command::ServerArgs), @@ -241,11 +238,8 @@ fn tokio_run_command(command: Command, server_connection: StdUnixStream) -> anyh } match command { - Command::User(user_args) => { - cli::user_command::handle_command(user_args, message_stream).await - } - Command::Db(db_args) => { - cli::database_command::handle_command(db_args, message_stream).await + Command::Client(client_args) => { + client::command::handle_command(client_args, message_stream).await } Command::Server(_) => unreachable!(), Command::GenerateCompletions(_) => unreachable!(),