Misc 7 #81
|
@ -15,7 +15,8 @@ use crate::{
|
|||
parse_privilege_table_cli_arg,
|
||||
},
|
||||
protocol::{
|
||||
print_create_databases_output_status, print_drop_databases_output_status,
|
||||
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, Request,
|
||||
Response,
|
||||
},
|
||||
|
@ -102,49 +103,57 @@ pub enum DatabaseCommand {
|
|||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DatabaseCreateArgs {
|
||||
/// The name of the database(s) to create.
|
||||
/// The name of the database(s) to create
|
||||
#[arg(num_args = 1..)]
|
||||
name: Vec<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DatabaseDropArgs {
|
||||
/// The name of the database(s) to drop.
|
||||
/// The name of the database(s) to drop
|
||||
#[arg(num_args = 1..)]
|
||||
name: Vec<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DatabaseShowArgs {
|
||||
/// The name of the database(s) to show.
|
||||
/// The name of the database(s) to show
|
||||
#[arg(num_args = 0..)]
|
||||
name: Vec<String>,
|
||||
|
||||
/// Whether to output the information in JSON format.
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DatabaseShowPrivsArgs {
|
||||
/// The name of the database(s) to show.
|
||||
/// The name of the database(s) to show
|
||||
#[arg(num_args = 0..)]
|
||||
name: Vec<String>,
|
||||
|
||||
/// Whether to output the information in JSON format.
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DatabaseEditPrivsArgs {
|
||||
/// The name of the database to edit privileges for.
|
||||
/// The name of the database to edit privileges for
|
||||
pub name: Option<String>,
|
||||
|
||||
#[arg(short, long, value_name = "[DATABASE:]USER:PRIVILEGES", num_args = 0..)]
|
||||
pub privs: Vec<String>,
|
||||
|
||||
/// Whether to output the information in JSON format.
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
pub json: bool,
|
||||
|
||||
|
@ -152,7 +161,7 @@ pub struct DatabaseEditPrivsArgs {
|
|||
#[arg(short, long)]
|
||||
pub editor: Option<String>,
|
||||
|
||||
/// Disable interactive confirmation before saving changes.
|
||||
/// Disable interactive confirmation before saving changes
|
||||
#[arg(short, long)]
|
||||
pub yes: bool,
|
||||
}
|
||||
|
@ -192,7 +201,11 @@ async fn create_databases(
|
|||
|
||||
server_connection.send(Request::Exit).await?;
|
||||
|
||||
print_create_databases_output_status(&result);
|
||||
if args.json {
|
||||
print_create_databases_output_status_json(&result);
|
||||
} else {
|
||||
print_create_databases_output_status(&result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -215,7 +228,11 @@ async fn drop_databases(
|
|||
|
||||
server_connection.send(Request::Exit).await?;
|
||||
|
||||
print_drop_databases_output_status(&result);
|
||||
if args.json {
|
||||
print_drop_databases_output_status_json(&result);
|
||||
} else {
|
||||
print_drop_databases_output_status(&result);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -232,6 +249,8 @@ async fn show_databases(
|
|||
|
||||
server_connection.send(message).await?;
|
||||
|
||||
// TODO: collect errors for json output.
|
||||
|
||||
let database_list = match server_connection.next().await {
|
||||
Some(Ok(Response::ListDatabases(databases))) => databases
|
||||
.into_iter()
|
||||
|
|
|
@ -4,9 +4,11 @@ use dialoguer::{Confirm, Password};
|
|||
use futures_util::{SinkExt, StreamExt};
|
||||
|
||||
use crate::core::protocol::{
|
||||
print_create_users_output_status, print_drop_users_output_status,
|
||||
print_lock_users_output_status, print_set_password_output_status,
|
||||
print_unlock_users_output_status, ClientToServerMessageStream, ListUsersError, Request,
|
||||
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, ClientToServerMessageStream, ListUsersError, Request,
|
||||
Response,
|
||||
};
|
||||
|
||||
|
@ -56,12 +58,22 @@ pub struct UserCreateArgs {
|
|||
/// 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<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
|
@ -70,6 +82,10 @@ pub struct UserPasswdArgs {
|
|||
|
||||
#[clap(short, long)]
|
||||
password_file: Option<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
|
@ -77,7 +93,8 @@ pub struct UserShowArgs {
|
|||
#[arg(num_args = 0..)]
|
||||
username: Vec<String>,
|
||||
|
||||
#[clap(short, long)]
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
|
@ -85,12 +102,20 @@ pub struct UserShowArgs {
|
|||
pub struct UserLockArgs {
|
||||
#[arg(num_args = 1..)]
|
||||
username: Vec<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct UserUnlockArgs {
|
||||
#[arg(num_args = 1..)]
|
||||
username: Vec<String>,
|
||||
|
||||
/// Print the information as JSON
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
}
|
||||
|
||||
pub async fn handle_command(
|
||||
|
@ -126,39 +151,43 @@ async fn create_users(
|
|||
response => return erroneous_server_response(response),
|
||||
};
|
||||
|
||||
print_create_users_output_status(&result);
|
||||
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::<Vec<_>>();
|
||||
let successfully_created_users = result
|
||||
.iter()
|
||||
.filter_map(|(username, result)| result.as_ref().ok().map(|_| username))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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.clone(), password);
|
||||
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.clone(), 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)
|
||||
if let Err(err) = server_connection.send(message).await {
|
||||
server_connection.close().await.ok();
|
||||
anyhow::bail!(err);
|
||||
}
|
||||
response => return erroneous_server_response(response),
|
||||
}
|
||||
|
||||
println!();
|
||||
match server_connection.next().await {
|
||||
Some(Ok(Response::PasswdUser(result))) => {
|
||||
print_set_password_output_status(&result, username)
|
||||
}
|
||||
response => return erroneous_server_response(response),
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,7 +218,11 @@ async fn drop_users(
|
|||
|
||||
server_connection.send(Request::Exit).await?;
|
||||
|
||||
print_drop_users_output_status(&result);
|
||||
if args.json {
|
||||
print_drop_users_output_status_json(&result);
|
||||
} else {
|
||||
print_drop_users_output_status(&result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -351,7 +384,11 @@ async fn lock_users(
|
|||
|
||||
server_connection.send(Request::Exit).await?;
|
||||
|
||||
print_lock_users_output_status(&result);
|
||||
if args.json {
|
||||
print_lock_users_output_status_json(&result);
|
||||
} else {
|
||||
print_lock_users_output_status(&result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -378,7 +415,11 @@ async fn unlock_users(
|
|||
|
||||
server_connection.send(Request::Exit).await?;
|
||||
|
||||
print_unlock_users_output_status(&result);
|
||||
if args.json {
|
||||
print_unlock_users_output_status_json(&result);
|
||||
} else {
|
||||
print_unlock_users_output_status(&result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
|||
use indoc::indoc;
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
core::{common::UnixUser, database_privileges::DatabasePrivilegeRowDiff},
|
||||
|
@ -141,6 +142,27 @@ pub fn print_create_databases_output_status(output: &CreateDatabasesOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_create_databases_output_status_json(output: &CreateDatabasesOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl CreateDatabaseError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
match self {
|
||||
|
@ -184,6 +206,27 @@ pub fn print_drop_databases_output_status(output: &DropDatabasesOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_drop_databases_output_status_json(output: &DropDatabasesOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl DropDatabaseError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
match self {
|
||||
|
@ -412,6 +455,27 @@ pub fn print_create_users_output_status(output: &CreateUsersOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_create_users_output_status_json(output: &CreateUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl CreateUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
match self {
|
||||
|
@ -453,6 +517,27 @@ pub fn print_drop_users_output_status(output: &DropUsersOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_drop_users_output_status_json(output: &DropUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl DropUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
match self {
|
||||
|
@ -531,6 +616,27 @@ pub fn print_lock_users_output_status(output: &LockUsersOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_lock_users_output_status_json(output: &LockUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl LockUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
match self {
|
||||
|
@ -574,6 +680,27 @@ pub fn print_unlock_users_output_status(output: &UnlockUsersOutput) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print_unlock_users_output_status_json(output: &UnlockUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.collect::<serde_json::Map<_, _>>();
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value)
|
||||
.unwrap_or("Failed to serialize result to JSON".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
impl UnlockUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
match self {
|
||||
|
|
Loading…
Reference in New Issue