Add more `--json` flags
This commit is contained in:
parent
cdb1fb4181
commit
338694a64e
|
@ -15,7 +15,8 @@ use crate::{
|
||||||
parse_privilege_table_cli_arg,
|
parse_privilege_table_cli_arg,
|
||||||
},
|
},
|
||||||
protocol::{
|
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,
|
print_modify_database_privileges_output_status, ClientToServerMessageStream, Request,
|
||||||
Response,
|
Response,
|
||||||
},
|
},
|
||||||
|
@ -102,49 +103,57 @@ pub enum DatabaseCommand {
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub 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>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub 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>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct DatabaseShowArgs {
|
pub struct DatabaseShowArgs {
|
||||||
/// 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>,
|
||||||
|
|
||||||
/// Whether to output the information in JSON format.
|
/// Print the information as JSON
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct DatabaseShowPrivsArgs {
|
pub struct DatabaseShowPrivsArgs {
|
||||||
/// 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>,
|
||||||
|
|
||||||
/// Whether to output the information in JSON format.
|
/// Print the information as JSON
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct DatabaseEditPrivsArgs {
|
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>,
|
pub name: Option<String>,
|
||||||
|
|
||||||
#[arg(short, long, value_name = "[DATABASE:]USER:PRIVILEGES", num_args = 0..)]
|
#[arg(short, long, value_name = "[DATABASE:]USER:PRIVILEGES", num_args = 0..)]
|
||||||
pub privs: Vec<String>,
|
pub privs: Vec<String>,
|
||||||
|
|
||||||
/// Whether to output the information in JSON format.
|
/// Print the information as JSON
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub json: bool,
|
pub json: bool,
|
||||||
|
|
||||||
|
@ -152,7 +161,7 @@ pub struct DatabaseEditPrivsArgs {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub editor: Option<String>,
|
pub editor: Option<String>,
|
||||||
|
|
||||||
/// Disable interactive confirmation before saving changes.
|
/// Disable interactive confirmation before saving changes
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub yes: bool,
|
pub yes: bool,
|
||||||
}
|
}
|
||||||
|
@ -192,7 +201,11 @@ async fn create_databases(
|
||||||
|
|
||||||
server_connection.send(Request::Exit).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -215,7 +228,11 @@ async fn drop_databases(
|
||||||
|
|
||||||
server_connection.send(Request::Exit).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -232,6 +249,8 @@ async fn show_databases(
|
||||||
|
|
||||||
server_connection.send(message).await?;
|
server_connection.send(message).await?;
|
||||||
|
|
||||||
|
// TODO: collect errors for json output.
|
||||||
|
|
||||||
let database_list = match server_connection.next().await {
|
let database_list = match server_connection.next().await {
|
||||||
Some(Ok(Response::ListDatabases(databases))) => databases
|
Some(Ok(Response::ListDatabases(databases))) => databases
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -4,9 +4,11 @@ use dialoguer::{Confirm, Password};
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
|
||||||
use crate::core::protocol::{
|
use crate::core::protocol::{
|
||||||
print_create_users_output_status, print_drop_users_output_status,
|
print_create_users_output_status, print_create_users_output_status_json,
|
||||||
print_lock_users_output_status, print_set_password_output_status,
|
print_drop_users_output_status, print_drop_users_output_status_json,
|
||||||
print_unlock_users_output_status, ClientToServerMessageStream, ListUsersError, Request,
|
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,
|
Response,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,12 +58,22 @@ pub struct UserCreateArgs {
|
||||||
/// Do not ask for a password, leave it unset
|
/// Do not ask for a password, leave it unset
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
no_password: bool,
|
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)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct UserDeleteArgs {
|
pub struct UserDeleteArgs {
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
@ -70,6 +82,10 @@ pub struct UserPasswdArgs {
|
||||||
|
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
password_file: Option<String>,
|
password_file: Option<String>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
@ -77,7 +93,8 @@ pub struct UserShowArgs {
|
||||||
#[arg(num_args = 0..)]
|
#[arg(num_args = 0..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
|
|
||||||
#[clap(short, long)]
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
json: bool,
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,12 +102,20 @@ pub struct UserShowArgs {
|
||||||
pub struct UserLockArgs {
|
pub struct UserLockArgs {
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct UserUnlockArgs {
|
pub struct UserUnlockArgs {
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
username: Vec<String>,
|
username: Vec<String>,
|
||||||
|
|
||||||
|
/// Print the information as JSON
|
||||||
|
#[arg(short, long)]
|
||||||
|
json: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_command(
|
pub async fn handle_command(
|
||||||
|
@ -126,39 +151,43 @@ async fn create_users(
|
||||||
response => return erroneous_server_response(response),
|
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
|
let successfully_created_users = result
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(username, result)| result.as_ref().ok().map(|_| username))
|
.filter_map(|(username, result)| result.as_ref().ok().map(|_| username))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for username in successfully_created_users {
|
for username in successfully_created_users {
|
||||||
if !args.no_password
|
if !args.no_password
|
||||||
&& Confirm::new()
|
&& Confirm::new()
|
||||||
.with_prompt(format!(
|
.with_prompt(format!(
|
||||||
"Do you want to set a password for user '{}'?",
|
"Do you want to set a password for user '{}'?",
|
||||||
username
|
username
|
||||||
))
|
))
|
||||||
.default(false)
|
.default(false)
|
||||||
.interact()?
|
.interact()?
|
||||||
{
|
{
|
||||||
let password = read_password_from_stdin_with_double_check(username)?;
|
let password = read_password_from_stdin_with_double_check(username)?;
|
||||||
let message = Request::PasswdUser(username.clone(), password);
|
let message = Request::PasswdUser(username.clone(), password);
|
||||||
|
|
||||||
if let Err(err) = server_connection.send(message).await {
|
if let Err(err) = server_connection.send(message).await {
|
||||||
server_connection.close().await.ok();
|
server_connection.close().await.ok();
|
||||||
anyhow::bail!(err);
|
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!();
|
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?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -351,7 +384,11 @@ async fn lock_users(
|
||||||
|
|
||||||
server_connection.send(Request::Exit).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -378,7 +415,11 @@ async fn unlock_users(
|
||||||
|
|
||||||
server_connection.send(Request::Exit).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{common::UnixUser, database_privileges::DatabasePrivilegeRowDiff},
|
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 {
|
impl CreateDatabaseError {
|
||||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||||
match self {
|
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 {
|
impl DropDatabaseError {
|
||||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||||
match self {
|
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 {
|
impl CreateUserError {
|
||||||
pub fn to_error_message(&self, username: &str) -> String {
|
pub fn to_error_message(&self, username: &str) -> String {
|
||||||
match self {
|
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 {
|
impl DropUserError {
|
||||||
pub fn to_error_message(&self, username: &str) -> String {
|
pub fn to_error_message(&self, username: &str) -> String {
|
||||||
match self {
|
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 {
|
impl LockUserError {
|
||||||
pub fn to_error_message(&self, username: &str) -> String {
|
pub fn to_error_message(&self, username: &str) -> String {
|
||||||
match self {
|
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 {
|
impl UnlockUserError {
|
||||||
pub fn to_error_message(&self, username: &str) -> String {
|
pub fn to_error_message(&self, username: &str) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
|
Loading…
Reference in New Issue