client: print only json for show-db/show-user/show-privs --json
All checks were successful
Build and test / check (push) Successful in 1m52s
Build and test / build (push) Successful in 3m4s
Build and test / test (push) Successful in 3m28s
Build and test / check-license (push) Successful in 5m47s
Build and test / docs (push) Successful in 4m45s

The earlier version would print out human readable errors before
printing the json, which was not ideal. This version prints out the
errors inside the json.
This commit is contained in:
2025-12-03 14:58:43 +09:00
parent 6a4a83367e
commit 32b70c44c6
6 changed files with 247 additions and 121 deletions

View File

@@ -1,14 +1,16 @@
use clap::Parser;
use clap_complete::ArgValueCompleter;
use futures_util::SinkExt;
use prettytable::{Cell, Row, Table};
use tokio_stream::StreamExt;
use crate::{
client::commands::erroneous_server_response,
core::{
completion::mysql_database_completer,
protocol::{ClientToServerMessageStream, Request, Response},
protocol::{
ClientToServerMessageStream, Request, Response, print_list_databases_output_status,
print_list_databases_output_status_json,
},
types::MySQLDatabase,
},
};
@@ -40,25 +42,13 @@ pub async fn show_databases(
server_connection.send(message).await?;
// TODO: collect errors for json output.
let mut contained_errors = false;
let database_list = match server_connection.next().await {
Some(Ok(Response::ListDatabases(databases))) => databases
.into_iter()
.filter_map(|(database_name, result)| match result {
Ok(database_row) => Some(database_row),
Err(err) => {
contained_errors = true;
eprintln!("{}", err.to_error_message(&database_name));
eprintln!("Skipping...");
println!();
None
}
})
.collect::<Vec<_>>(),
let databases = match server_connection.next().await {
Some(Ok(Response::ListDatabases(databases))) => databases,
Some(Ok(Response::ListAllDatabases(database_list))) => match database_list {
Ok(list) => list,
Ok(list) => list
.into_iter()
.map(|db| (db.database.clone(), Ok(db)))
.collect(),
Err(err) => {
server_connection.send(Request::Exit).await?;
return Err(
@@ -72,19 +62,12 @@ pub async fn show_databases(
server_connection.send(Request::Exit).await?;
if args.json {
println!("{}", serde_json::to_string_pretty(&database_list)?);
} else if database_list.is_empty() {
println!("No databases to show.");
print_list_databases_output_status_json(&databases);
} else {
let mut table = Table::new();
table.add_row(Row::new(vec![Cell::new("Database")]));
for db in database_list {
table.add_row(row![db.database]);
}
table.printstd();
print_list_databases_output_status(&databases);
}
if args.fail && contained_errors {
if args.fail && databases.values().any(|res| res.is_err()) {
std::process::exit(1);
}

View File

@@ -1,16 +1,17 @@
use clap::Parser;
use clap_complete::ArgValueCompleter;
use futures_util::SinkExt;
use prettytable::{Cell, Row, Table};
use itertools::Itertools;
use tokio_stream::StreamExt;
use crate::{
client::commands::erroneous_server_response,
core::{
common::yn,
completion::mysql_database_completer,
database_privileges::{DATABASE_PRIVILEGE_FIELDS, db_priv_field_human_readable_name},
protocol::{ClientToServerMessageStream, Request, Response},
protocol::{
ClientToServerMessageStream, Request, Response, print_list_privileges_output_status,
print_list_privileges_output_status_json,
},
types::MySQLDatabase,
},
};
@@ -41,24 +42,16 @@ pub async fn show_database_privileges(
};
server_connection.send(message).await?;
let mut contained_errors = false;
let privilege_data = match server_connection.next().await {
Some(Ok(Response::ListPrivileges(databases))) => databases
.into_iter()
.filter_map(|(database_name, result)| match result {
Ok(privileges) => Some(privileges),
Err(err) => {
contained_errors = true;
eprintln!("{}", err.to_error_message(&database_name));
eprintln!("Skipping...");
println!();
None
}
})
.flatten()
.collect::<Vec<_>>(),
Some(Ok(Response::ListPrivileges(databases))) => databases,
Some(Ok(Response::ListAllPrivileges(privilege_rows))) => match privilege_rows {
Ok(list) => list,
Ok(list) => list
.into_iter()
.map(|row| (row.db.clone(), row))
.into_group_map()
.into_iter()
.map(|(db, rows)| (db, Ok(rows)))
.collect(),
Err(err) => {
server_connection.send(Request::Exit).await?;
return Err(anyhow::anyhow!(err.to_error_message())
@@ -71,40 +64,12 @@ pub async fn show_database_privileges(
server_connection.send(Request::Exit).await?;
if args.json {
println!("{}", serde_json::to_string_pretty(&privilege_data)?);
} else if privilege_data.is_empty() {
println!("No database privileges to show.");
print_list_privileges_output_status_json(&privilege_data);
} else {
let mut table = Table::new();
table.add_row(Row::new(
DATABASE_PRIVILEGE_FIELDS
.into_iter()
.map(db_priv_field_human_readable_name)
.map(|name| Cell::new(&name))
.collect(),
));
for row in privilege_data {
table.add_row(row![
row.db,
row.user,
c->yn(row.select_priv),
c->yn(row.insert_priv),
c->yn(row.update_priv),
c->yn(row.delete_priv),
c->yn(row.create_priv),
c->yn(row.drop_priv),
c->yn(row.alter_priv),
c->yn(row.index_priv),
c->yn(row.create_tmp_table_priv),
c->yn(row.lock_tables_priv),
c->yn(row.references_priv),
]);
}
table.printstd();
print_list_privileges_output_status(&privilege_data);
}
if args.fail && contained_errors {
if args.fail && privilege_data.values().any(|res| res.is_err()) {
std::process::exit(1);
}

View File

@@ -1,4 +1,3 @@
use anyhow::Context;
use clap::Parser;
use clap_complete::ArgValueCompleter;
use futures_util::SinkExt;
@@ -8,7 +7,10 @@ use crate::{
client::commands::erroneous_server_response,
core::{
completion::mysql_user_completer,
protocol::{ClientToServerMessageStream, Request, Response},
protocol::{
ClientToServerMessageStream, Request, Response, print_list_users_output_status,
print_list_users_output_status_json,
},
types::MySQLUser,
},
};
@@ -43,22 +45,13 @@ pub async fn show_users(
anyhow::bail!(err);
}
let mut contained_errors = false;
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) => {
contained_errors = true;
eprintln!("{}", err.to_error_message(&username));
eprintln!("Skipping...");
None
}
})
.collect::<Vec<_>>(),
Some(Ok(Response::ListUsers(users))) => users,
Some(Ok(Response::ListAllUsers(users))) => match users {
Ok(users) => users,
Ok(users) => users
.into_iter()
.map(|user| (user.user.clone(), Ok(user)))
.collect(),
Err(err) => {
server_connection.send(Request::Exit).await?;
return Err(
@@ -72,32 +65,12 @@ pub async fn show_users(
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.");
print_list_users_output_status_json(&users);
} 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();
print_list_users_output_status(&users);
}
if args.fail && contained_errors {
if args.fail && users.values().any(|result| result.is_err()) {
std::process::exit(1);
}