1
use clap::Parser;
2
use dialoguer::Confirm;
3
use futures_util::SinkExt;
4
use tokio_stream::StreamExt;
5

            
6
use crate::{
7
    client::commands::{erroneous_server_response, read_password_from_stdin_with_double_check},
8
    core::{
9
        protocol::{
10
            ClientToServerMessageStream, Request, Response, print_create_users_output_status,
11
            print_create_users_output_status_json, print_set_password_output_status,
12
        },
13
        types::MySQLUser,
14
    },
15
};
16

            
17
#[derive(Parser, Debug, Clone)]
18
pub struct CreateUserArgs {
19
    #[arg(num_args = 1..)]
20
    username: Vec<MySQLUser>,
21

            
22
    /// Do not ask for a password, leave it unset
23
    #[clap(long)]
24
    no_password: bool,
25

            
26
    /// Print the information as JSON
27
    ///
28
    /// Note that this implies `--no-password`, since the command will become non-interactive.
29
    #[arg(short, long)]
30
    json: bool,
31
}
32

            
33
pub async fn create_users(
34
    args: CreateUserArgs,
35
    mut server_connection: ClientToServerMessageStream,
36
) -> anyhow::Result<()> {
37
    if args.username.is_empty() {
38
        anyhow::bail!("No usernames provided");
39
    }
40

            
41
    let message = Request::CreateUsers(args.username.to_owned());
42
    if let Err(err) = server_connection.send(message).await {
43
        server_connection.close().await.ok();
44
        anyhow::bail!(anyhow::Error::from(err).context("Failed to communicate with server"));
45
    }
46

            
47
    let result = match server_connection.next().await {
48
        Some(Ok(Response::CreateUsers(result))) => result,
49
        response => return erroneous_server_response(response),
50
    };
51

            
52
    if args.json {
53
        print_create_users_output_status_json(&result);
54
    } else {
55
        print_create_users_output_status(&result);
56

            
57
        let successfully_created_users = result
58
            .iter()
59
            .filter_map(|(username, result)| result.as_ref().ok().map(|_| username))
60
            .collect::<Vec<_>>();
61

            
62
        for username in successfully_created_users {
63
            if !args.no_password
64
                && Confirm::new()
65
                    .with_prompt(format!(
66
                        "Do you want to set a password for user '{}'?",
67
                        username
68
                    ))
69
                    .default(false)
70
                    .interact()?
71
            {
72
                let password = read_password_from_stdin_with_double_check(username)?;
73
                let message = Request::PasswdUser((username.to_owned(), password));
74

            
75
                if let Err(err) = server_connection.send(message).await {
76
                    server_connection.close().await.ok();
77
                    anyhow::bail!(err);
78
                }
79

            
80
                match server_connection.next().await {
81
                    Some(Ok(Response::SetUserPassword(result))) => {
82
                        print_set_password_output_status(&result, username)
83
                    }
84
                    response => return erroneous_server_response(response),
85
                }
86

            
87
                println!();
88
            }
89
        }
90
    }
91

            
92
    server_connection.send(Request::Exit).await?;
93

            
94
    Ok(())
95
}