client: add prefix completer for create-{db,user}
All checks were successful
All checks were successful
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use clap_complete::ArgValueCompleter;
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::commands::{erroneous_server_response, print_authorization_owner_hint},
|
client::commands::{erroneous_server_response, print_authorization_owner_hint},
|
||||||
core::{
|
core::{
|
||||||
|
completion::prefix_completer,
|
||||||
protocol::{
|
protocol::{
|
||||||
ClientToServerMessageStream, CreateDatabaseError, Request, Response,
|
ClientToServerMessageStream, CreateDatabaseError, Request, Response,
|
||||||
print_create_databases_output_status, print_create_databases_output_status_json,
|
print_create_databases_output_status, print_create_databases_output_status_json,
|
||||||
@@ -18,6 +20,7 @@ use crate::{
|
|||||||
pub struct CreateDbArgs {
|
pub struct CreateDbArgs {
|
||||||
/// The MySQL database(s) to create
|
/// The MySQL database(s) to create
|
||||||
#[arg(num_args = 1.., value_name = "DB_NAME")]
|
#[arg(num_args = 1.., value_name = "DB_NAME")]
|
||||||
|
#[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(prefix_completer)))]
|
||||||
name: Vec<MySQLDatabase>,
|
name: Vec<MySQLDatabase>,
|
||||||
|
|
||||||
/// Print the information as JSON
|
/// Print the information as JSON
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use clap_complete::ArgValueCompleter;
|
||||||
use dialoguer::Confirm;
|
use dialoguer::Confirm;
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
@@ -9,6 +10,7 @@ use crate::{
|
|||||||
read_password_from_stdin_with_double_check,
|
read_password_from_stdin_with_double_check,
|
||||||
},
|
},
|
||||||
core::{
|
core::{
|
||||||
|
completion::prefix_completer,
|
||||||
protocol::{
|
protocol::{
|
||||||
ClientToServerMessageStream, CreateUserError, Request, Response,
|
ClientToServerMessageStream, CreateUserError, Request, Response,
|
||||||
print_create_users_output_status, print_create_users_output_status_json,
|
print_create_users_output_status, print_create_users_output_status_json,
|
||||||
@@ -22,6 +24,7 @@ use crate::{
|
|||||||
pub struct CreateUserArgs {
|
pub struct CreateUserArgs {
|
||||||
/// The MySQL user(s) to create
|
/// The MySQL user(s) to create
|
||||||
#[arg(num_args = 1.., value_name = "USER_NAME")]
|
#[arg(num_args = 1.., value_name = "USER_NAME")]
|
||||||
|
#[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(prefix_completer)))]
|
||||||
username: Vec<MySQLUser>,
|
username: Vec<MySQLUser>,
|
||||||
|
|
||||||
/// Do not ask for a password, leave it unset
|
/// Do not ask for a password, leave it unset
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
core::{
|
core::{
|
||||||
bootstrap::bootstrap_server_connection_and_drop_privileges,
|
bootstrap::bootstrap_server_connection_and_drop_privileges,
|
||||||
completion::mysql_database_completer,
|
completion::{mysql_database_completer, prefix_completer},
|
||||||
database_privileges::DatabasePrivilegeRow,
|
database_privileges::DatabasePrivilegeRow,
|
||||||
protocol::{
|
protocol::{
|
||||||
ClientToServerMessageStream, ListPrivilegesError, Request, Response,
|
ClientToServerMessageStream, ListPrivilegesError, Request, Response,
|
||||||
@@ -124,6 +124,7 @@ pub enum Command {
|
|||||||
pub struct CreateArgs {
|
pub struct CreateArgs {
|
||||||
/// The name of the DATABASE(s) to create.
|
/// The name of the DATABASE(s) to create.
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
|
#[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(prefix_completer)))]
|
||||||
name: Vec<MySQLDatabase>,
|
name: Vec<MySQLDatabase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
core::{
|
core::{
|
||||||
bootstrap::bootstrap_server_connection_and_drop_privileges,
|
bootstrap::bootstrap_server_connection_and_drop_privileges,
|
||||||
completion::mysql_user_completer,
|
completion::{mysql_user_completer, prefix_completer},
|
||||||
protocol::{
|
protocol::{
|
||||||
ClientToServerMessageStream, Request, Response, create_client_to_server_message_stream,
|
ClientToServerMessageStream, Request, Response, create_client_to_server_message_stream,
|
||||||
},
|
},
|
||||||
@@ -87,6 +87,7 @@ pub enum Command {
|
|||||||
pub struct CreateArgs {
|
pub struct CreateArgs {
|
||||||
/// The name of the USER(s) to create.
|
/// The name of the USER(s) to create.
|
||||||
#[arg(num_args = 1..)]
|
#[arg(num_args = 1..)]
|
||||||
|
#[cfg_attr(not(feature = "suid-sgid-mode"), arg(add = ArgValueCompleter::new(prefix_completer)))]
|
||||||
name: Vec<MySQLUser>,
|
name: Vec<MySQLUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
mod mysql_database_completer;
|
mod mysql_database_completer;
|
||||||
mod mysql_user_completer;
|
mod mysql_user_completer;
|
||||||
|
mod prefix_completer;
|
||||||
|
|
||||||
pub use mysql_database_completer::*;
|
pub use mysql_database_completer::*;
|
||||||
pub use mysql_user_completer::*;
|
pub use mysql_user_completer::*;
|
||||||
|
pub use prefix_completer::*;
|
||||||
|
|||||||
75
src/core/completion/prefix_completer.rs
Normal file
75
src/core/completion/prefix_completer.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use clap_complete::CompletionCandidate;
|
||||||
|
use clap_verbosity_flag::Verbosity;
|
||||||
|
use futures_util::SinkExt;
|
||||||
|
use tokio::net::UnixStream as TokioUnixStream;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
client::commands::erroneous_server_response,
|
||||||
|
core::{
|
||||||
|
bootstrap::bootstrap_server_connection_and_drop_privileges,
|
||||||
|
protocol::{Request, Response, create_client_to_server_message_stream},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn prefix_completer(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
|
||||||
|
match tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Ok(runtime) => match runtime.block_on(prefix_completer_(current)) {
|
||||||
|
Ok(completions) => completions,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error getting prefix completions: {}", err);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error starting Tokio runtime: {}", err);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to the server to get MySQL user completions.
|
||||||
|
async fn prefix_completer_(_current: &std::ffi::OsStr) -> anyhow::Result<Vec<CompletionCandidate>> {
|
||||||
|
let server_connection =
|
||||||
|
bootstrap_server_connection_and_drop_privileges(None, None, Verbosity::new(0, 1))?;
|
||||||
|
|
||||||
|
let tokio_socket = TokioUnixStream::from_std(server_connection)?;
|
||||||
|
let mut server_connection = create_client_to_server_message_stream(tokio_socket);
|
||||||
|
|
||||||
|
while let Some(Ok(message)) = server_connection.next().await {
|
||||||
|
match message {
|
||||||
|
Response::Error(err) => {
|
||||||
|
anyhow::bail!("{}", err);
|
||||||
|
}
|
||||||
|
Response::Ready => break,
|
||||||
|
message => {
|
||||||
|
eprintln!("Unexpected message from server: {:?}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = Request::ListValidNamePrefixes;
|
||||||
|
|
||||||
|
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::ListValidNamePrefixes(prefixes))) => prefixes,
|
||||||
|
response => return erroneous_server_response(response).map(|_| vec![]),
|
||||||
|
};
|
||||||
|
|
||||||
|
server_connection.send(Request::Exit).await?;
|
||||||
|
|
||||||
|
let result = result
|
||||||
|
.into_iter()
|
||||||
|
.map(|prefix| prefix + "_")
|
||||||
|
.map(CompletionCandidate::new)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user