1
mod create_db;
2
mod create_user;
3
mod drop_db;
4
mod drop_user;
5
mod edit_privs;
6
mod lock_user;
7
mod passwd_user;
8
mod show_db;
9
mod show_privs;
10
mod show_user;
11
mod unlock_user;
12

            
13
pub use create_db::*;
14
pub use create_user::*;
15
pub use drop_db::*;
16
pub use drop_user::*;
17
pub use edit_privs::*;
18
pub use lock_user::*;
19
pub use passwd_user::*;
20
pub use show_db::*;
21
pub use show_privs::*;
22
pub use show_user::*;
23
pub use unlock_user::*;
24

            
25
use clap::Parser;
26

            
27
use crate::core::protocol::{ClientToServerMessageStream, Response};
28

            
29
#[derive(Parser, Debug, Clone)]
30
pub enum ClientCommand {
31
    /// Create one or more databases
32
    #[command()]
33
    CreateDb(CreateDbArgs),
34

            
35
    /// Delete one or more databases
36
    #[command()]
37
    DropDb(DropDbArgs),
38

            
39
    /// Print information about one or more databases
40
    ///
41
    /// If no database name is provided, all databases you have access will be shown.
42
    #[command()]
43
    ShowDb(ShowDbArgs),
44

            
45
    /// Print user privileges for one or more databases
46
    ///
47
    /// If no database names are provided, all databases you have access to will be shown.
48
    #[command()]
49
    ShowPrivs(ShowPrivsArgs),
50

            
51
    /// Change user privileges for one or more databases. See `edit-privs --help` for details.
52
    ///
53
    /// This command has two modes of operation:
54
    ///
55
    /// 1. Interactive mode: If nothing else is specified, the user will be prompted to edit the privileges using a text editor.
56
    ///
57
    ///    You can configure your preferred text editor by setting the `VISUAL` or `EDITOR` environment variables.
58
    ///
59
    ///    Follow the instructions inside the editor for more information.
60
    ///
61
    /// 2. Non-interactive mode: If the `-p` flag is specified, the user can write privileges using arguments.
62
    ///
63
    ///    The privilege arguments should be formatted as `<db>:<user>+<privileges>-<privileges>`
64
    ///    where the privileges are a string of characters, each representing a single privilege.
65
    ///    The character `A` is an exception - it represents all privileges.
66
    ///
67
    ///    The character-to-privilege mapping is defined as follows:
68
    ///
69
    ///    - `s` - SELECT
70
    ///    - `i` - INSERT
71
    ///    - `u` - UPDATE
72
    ///    - `d` - DELETE
73
    ///    - `c` - CREATE
74
    ///    - `D` - DROP
75
    ///    - `a` - ALTER
76
    ///    - `I` - INDEX
77
    ///    - `t` - CREATE TEMPORARY TABLES
78
    ///    - `l` - LOCK TABLES
79
    ///    - `r` - REFERENCES
80
    ///    - `A` - ALL PRIVILEGES
81
    ///
82
    ///   If you provide a database name, you can omit it from the privilege string,
83
    ///   e.g. `edit-privs my_db -p my_user+siu` is equivalent to `edit-privs -p my_db:my_user:siu`.
84
    ///   While it doesn't make much of a difference for a single edit, it can be useful for editing multiple users
85
    ///   on the same database at once.
86
    ///
87
    ///   Example usage of non-interactive mode:
88
    ///
89
    ///     Enable privileges `SELECT`, `INSERT`, and `UPDATE` for user `my_user` on database `my_db`:
90
    ///
91
    ///       `muscle edit-privs -p my_db:my_user:siu`
92
    ///
93
    ///     Enable all privileges for user `my_other_user` on database `my_other_db`:
94
    ///
95
    ///       `muscle edit-privs -p my_other_db:my_other_user:A`
96
    ///
97
    ///     Set miscellaneous privileges for multiple users on database `my_db`:
98
    ///
99
    ///       `muscle edit-privs my_db -p my_user:siu my_other_user:ct``
100
    ///
101
    #[command(verbatim_doc_comment)]
102
    EditPrivs(EditPrivsArgs),
103

            
104
    /// Create one or more users
105
    #[command()]
106
    CreateUser(CreateUserArgs),
107

            
108
    /// Delete one or more users
109
    #[command()]
110
    DropUser(DropUserArgs),
111

            
112
    /// Change the MySQL password for a user
113
    #[command()]
114
    PasswdUser(PasswdUserArgs),
115

            
116
    /// Print information about one or more users
117
    ///
118
    /// If no username is provided, all users you have access will be shown.
119
    #[command()]
120
    ShowUser(ShowUserArgs),
121

            
122
    /// Lock account for one or more users
123
    #[command()]
124
    LockUser(LockUserArgs),
125

            
126
    /// Unlock account for one or more users
127
    #[command()]
128
    UnlockUser(UnlockUserArgs),
129
}
130

            
131
pub async fn handle_command(
132
    command: ClientCommand,
133
    server_connection: ClientToServerMessageStream,
134
) -> anyhow::Result<()> {
135
    match command {
136
        ClientCommand::CreateDb(args) => create_databases(args, server_connection).await,
137
        ClientCommand::DropDb(args) => drop_databases(args, server_connection).await,
138
        ClientCommand::ShowDb(args) => show_databases(args, server_connection).await,
139
        ClientCommand::ShowPrivs(args) => show_database_privileges(args, server_connection).await,
140
        ClientCommand::EditPrivs(args) => edit_database_privileges(args, server_connection).await,
141
        ClientCommand::CreateUser(args) => create_users(args, server_connection).await,
142
        ClientCommand::DropUser(args) => drop_users(args, server_connection).await,
143
        ClientCommand::PasswdUser(args) => passwd_user(args, server_connection).await,
144
        ClientCommand::ShowUser(args) => show_users(args, server_connection).await,
145
        ClientCommand::LockUser(args) => lock_users(args, server_connection).await,
146
        ClientCommand::UnlockUser(args) => unlock_users(args, server_connection).await,
147
    }
148
}
149

            
150
pub fn erroneous_server_response(
151
    response: Option<Result<Response, std::io::Error>>,
152
) -> anyhow::Result<()> {
153
    match response {
154
        Some(Ok(Response::Error(e))) => {
155
            anyhow::bail!("Server returned error: {}", e);
156
        }
157
        Some(Err(e)) => {
158
            anyhow::bail!(e);
159
        }
160
        Some(response) => {
161
            anyhow::bail!("Unexpected response from server: {:?}", response);
162
        }
163
        None => {
164
            anyhow::bail!("No response from server");
165
        }
166
    }
167
}