client: print only json for show-db/show-user --json
All checks were successful
Build and test / check (push) Successful in 1m56s
Build and test / build (push) Successful in 3m56s
Build and test / check-license (push) Successful in 4m52s
Build and test / test (push) Successful in 3m27s
Build and test / docs (push) Successful in 6m16s

This commit is contained in:
2025-12-03 14:58:43 +09:00
parent 6a4a83367e
commit 9147c0fd3e
4 changed files with 146 additions and 69 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,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);
}

View File

@@ -1,6 +1,8 @@
use std::collections::BTreeMap;
use prettytable::{Cell, Row, Table};
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
core::{
@@ -22,6 +24,57 @@ pub enum ListDatabasesError {
MySqlError(String),
}
pub fn print_list_databases_output_status(output: &ListDatabasesResponse) {
let mut final_database_list: Vec<&DatabaseRow> = Vec::new();
for (db_name, db_result) in output {
match db_result {
Ok(db_row) => final_database_list.push(db_row),
Err(err) => {
eprintln!("{}", err.to_error_message(db_name));
eprintln!("Skipping...");
}
}
}
if final_database_list.is_empty() {
println!("No databases to show.");
} else {
let mut table = Table::new();
table.add_row(Row::new(vec![Cell::new("Database")]));
for db in final_database_list {
table.add_row(row![db.database]);
}
table.printstd();
}
}
pub fn print_list_databases_output_status_json(output: &ListDatabasesResponse) {
let value = output
.iter()
.map(|(name, result)| match result {
Ok(_row) => (
name.to_string(),
json!({
"status": "success",
// NOTE: there will likely be more data to include here in the future
}),
),
Err(err) => (
name.to_string(),
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 ListDatabasesError {
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
match self {

View File

@@ -1,6 +1,8 @@
use std::collections::BTreeMap;
use prettytable::Table;
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
core::{
@@ -22,6 +24,72 @@ pub enum ListUsersError {
MySqlError(String),
}
pub fn print_list_users_output_status(output: &ListUsersResponse) {
let mut final_user_list: Vec<&DatabaseUser> = Vec::new();
for (db_name, db_result) in output {
match db_result {
Ok(db_row) => final_user_list.push(db_row),
Err(err) => {
eprintln!("{}", err.to_error_message(db_name));
eprintln!("Skipping...");
}
}
}
if final_user_list.is_empty() {
println!("No users to show.");
} else {
let mut table = Table::new();
table.add_row(row![
"User",
"Password is set",
"Locked",
"Databases where user has privileges"
]);
for user in final_user_list {
table.add_row(row![
user.user,
user.has_password,
user.is_locked,
user.databases.join("\n")
]);
}
table.printstd();
}
}
pub fn print_list_users_output_status_json(output: &ListUsersResponse) {
let value = output
.iter()
.map(|(name, result)| match result {
Ok(row) => (
name.to_string(),
json!({
"status": "success",
"value": {
"user": row.user,
"has_password": row.has_password,
"is_locked": row.is_locked,
"databases": row.databases,
}
}),
),
Err(err) => (
name.to_string(),
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 ListUsersError {
pub fn to_error_message(&self, username: &MySQLUser) -> String {
match self {