mysqladm/core/protocol/
server_responses.rs

1use std::collections::BTreeMap;
2
3use indoc::indoc;
4use itertools::Itertools;
5use serde::{Deserialize, Serialize};
6use serde_json::json;
7
8use crate::{
9    core::{
10        common::UnixUser,
11        database_privileges::{DatabasePrivilegeRow, DatabasePrivilegeRowDiff},
12    },
13    server::sql::{database_operations::DatabaseRow, user_operations::DatabaseUser},
14};
15
16use super::{MySQLDatabase, MySQLUser};
17
18/// This enum is used to differentiate between database and user operations.
19/// Their output are very similar, but there are slight differences in the words used.
20#[derive(Debug, PartialEq, Eq, Clone, Copy)]
21pub enum DbOrUser {
22    Database,
23    User,
24}
25
26impl DbOrUser {
27    pub fn lowercased(&self) -> &'static str {
28        match self {
29            DbOrUser::Database => "database",
30            DbOrUser::User => "user",
31        }
32    }
33
34    pub fn capitalized(&self) -> &'static str {
35        match self {
36            DbOrUser::Database => "Database",
37            DbOrUser::User => "User",
38        }
39    }
40}
41
42#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
43pub enum NameValidationError {
44    EmptyString,
45    InvalidCharacters,
46    TooLong,
47}
48
49impl NameValidationError {
50    pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
51        match self {
52            NameValidationError::EmptyString => {
53                format!("{} name cannot be empty.", db_or_user.capitalized()).to_owned()
54            }
55            NameValidationError::TooLong => format!(
56                "{} is too long. Maximum length is 64 characters.",
57                db_or_user.capitalized()
58            )
59            .to_owned(),
60            NameValidationError::InvalidCharacters => format!(
61                indoc! {r#"
62                  Invalid characters in {} name: '{}'
63
64                  Only A-Z, a-z, 0-9, _ (underscore) and - (dash) are permitted.
65                "#},
66                db_or_user.lowercased(),
67                name
68            )
69            .to_owned(),
70        }
71    }
72}
73
74impl OwnerValidationError {
75    pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
76        let user = UnixUser::from_enviroment();
77
78        let UnixUser {
79            username,
80            mut groups,
81        } = user.unwrap_or(UnixUser {
82            username: "???".to_string(),
83            groups: vec![],
84        });
85
86        groups.sort();
87
88        match self {
89            OwnerValidationError::NoMatch => format!(
90                indoc! {r#"
91                  Invalid {} name prefix: '{}' does not match your username or any of your groups.
92                  Are you sure you are allowed to create {} names with this prefix?
93                  The format should be: <prefix>_<{} name>
94
95                  Allowed prefixes:
96                    - {}
97                  {}
98                "#},
99                db_or_user.lowercased(),
100                name,
101                db_or_user.lowercased(),
102                db_or_user.lowercased(),
103                username,
104                groups
105                    .into_iter()
106                    .filter(|g| g != &username)
107                    .map(|g| format!("  - {}", g))
108                    .join("\n"),
109            )
110            .to_owned(),
111            OwnerValidationError::StringEmpty => format!(
112                "'{}' is not a valid {} name.",
113                name,
114                db_or_user.lowercased()
115            )
116            .to_string(),
117        }
118    }
119}
120
121#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
122pub enum OwnerValidationError {
123    // The name is valid, but none of the given prefixes matched the name
124    NoMatch,
125
126    // The name is empty, which is invalid
127    StringEmpty,
128}
129
130pub type CreateDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), CreateDatabaseError>>;
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132pub enum CreateDatabaseError {
133    SanitizationError(NameValidationError),
134    OwnershipError(OwnerValidationError),
135    DatabaseAlreadyExists,
136    MySqlError(String),
137}
138
139pub fn print_create_databases_output_status(output: &CreateDatabasesOutput) {
140    for (database_name, result) in output {
141        match result {
142            Ok(()) => {
143                println!("Database '{}' created successfully.", database_name);
144            }
145            Err(err) => {
146                println!("{}", err.to_error_message(database_name));
147                println!("Skipping...");
148            }
149        }
150        println!();
151    }
152}
153
154pub fn print_create_databases_output_status_json(output: &CreateDatabasesOutput) {
155    let value = output
156        .iter()
157        .map(|(name, result)| match result {
158            Ok(()) => (name.to_string(), json!({ "status": "success" })),
159            Err(err) => (
160                name.to_string(),
161                json!({
162                  "status": "error",
163                  "error": err.to_error_message(name),
164                }),
165            ),
166        })
167        .collect::<serde_json::Map<_, _>>();
168    println!(
169        "{}",
170        serde_json::to_string_pretty(&value)
171            .unwrap_or("Failed to serialize result to JSON".to_string())
172    );
173}
174
175impl CreateDatabaseError {
176    pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
177        match self {
178            CreateDatabaseError::SanitizationError(err) => {
179                err.to_error_message(database_name, DbOrUser::Database)
180            }
181            CreateDatabaseError::OwnershipError(err) => {
182                err.to_error_message(database_name, DbOrUser::Database)
183            }
184            CreateDatabaseError::DatabaseAlreadyExists => {
185                format!("Database {} already exists.", database_name)
186            }
187            CreateDatabaseError::MySqlError(err) => {
188                format!("MySQL error: {}", err)
189            }
190        }
191    }
192}
193
194pub type DropDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), DropDatabaseError>>;
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196pub enum DropDatabaseError {
197    SanitizationError(NameValidationError),
198    OwnershipError(OwnerValidationError),
199    DatabaseDoesNotExist,
200    MySqlError(String),
201}
202
203pub fn print_drop_databases_output_status(output: &DropDatabasesOutput) {
204    for (database_name, result) in output {
205        match result {
206            Ok(()) => {
207                println!(
208                    "Database '{}' dropped successfully.",
209                    database_name.as_str()
210                );
211            }
212            Err(err) => {
213                println!("{}", err.to_error_message(database_name));
214                println!("Skipping...");
215            }
216        }
217        println!();
218    }
219}
220
221pub fn print_drop_databases_output_status_json(output: &DropDatabasesOutput) {
222    let value = output
223        .iter()
224        .map(|(name, result)| match result {
225            Ok(()) => (name.to_string(), json!({ "status": "success" })),
226            Err(err) => (
227                name.to_string(),
228                json!({
229                  "status": "error",
230                  "error": err.to_error_message(name),
231                }),
232            ),
233        })
234        .collect::<serde_json::Map<_, _>>();
235    println!(
236        "{}",
237        serde_json::to_string_pretty(&value)
238            .unwrap_or("Failed to serialize result to JSON".to_string())
239    );
240}
241
242impl DropDatabaseError {
243    pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
244        match self {
245            DropDatabaseError::SanitizationError(err) => {
246                err.to_error_message(database_name, DbOrUser::Database)
247            }
248            DropDatabaseError::OwnershipError(err) => {
249                err.to_error_message(database_name, DbOrUser::Database)
250            }
251            DropDatabaseError::DatabaseDoesNotExist => {
252                format!("Database {} does not exist.", database_name)
253            }
254            DropDatabaseError::MySqlError(err) => {
255                format!("MySQL error: {}", err)
256            }
257        }
258    }
259}
260
261pub type ListDatabasesOutput = BTreeMap<MySQLDatabase, Result<DatabaseRow, ListDatabasesError>>;
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub enum ListDatabasesError {
264    SanitizationError(NameValidationError),
265    OwnershipError(OwnerValidationError),
266    DatabaseDoesNotExist,
267    MySqlError(String),
268}
269
270impl ListDatabasesError {
271    pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
272        match self {
273            ListDatabasesError::SanitizationError(err) => {
274                err.to_error_message(database_name, DbOrUser::Database)
275            }
276            ListDatabasesError::OwnershipError(err) => {
277                err.to_error_message(database_name, DbOrUser::Database)
278            }
279            ListDatabasesError::DatabaseDoesNotExist => {
280                format!("Database '{}' does not exist.", database_name)
281            }
282            ListDatabasesError::MySqlError(err) => {
283                format!("MySQL error: {}", err)
284            }
285        }
286    }
287}
288
289pub type ListAllDatabasesOutput = Result<Vec<DatabaseRow>, ListAllDatabasesError>;
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
291pub enum ListAllDatabasesError {
292    MySqlError(String),
293}
294
295impl ListAllDatabasesError {
296    pub fn to_error_message(&self) -> String {
297        match self {
298            ListAllDatabasesError::MySqlError(err) => format!("MySQL error: {}", err),
299        }
300    }
301}
302
303// TODO: merge all rows into a single collection.
304//       they already contain which database they belong to.
305//       no need to index by database name.
306
307pub type GetDatabasesPrivilegeData =
308    BTreeMap<MySQLDatabase, Result<Vec<DatabasePrivilegeRow>, GetDatabasesPrivilegeDataError>>;
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
310pub enum GetDatabasesPrivilegeDataError {
311    SanitizationError(NameValidationError),
312    OwnershipError(OwnerValidationError),
313    DatabaseDoesNotExist,
314    MySqlError(String),
315}
316
317impl GetDatabasesPrivilegeDataError {
318    pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
319        match self {
320            GetDatabasesPrivilegeDataError::SanitizationError(err) => {
321                err.to_error_message(database_name, DbOrUser::Database)
322            }
323            GetDatabasesPrivilegeDataError::OwnershipError(err) => {
324                err.to_error_message(database_name, DbOrUser::Database)
325            }
326            GetDatabasesPrivilegeDataError::DatabaseDoesNotExist => {
327                format!("Database '{}' does not exist.", database_name)
328            }
329            GetDatabasesPrivilegeDataError::MySqlError(err) => {
330                format!("MySQL error: {}", err)
331            }
332        }
333    }
334}
335
336pub type GetAllDatabasesPrivilegeData =
337    Result<Vec<DatabasePrivilegeRow>, GetAllDatabasesPrivilegeDataError>;
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
339pub enum GetAllDatabasesPrivilegeDataError {
340    MySqlError(String),
341}
342
343impl GetAllDatabasesPrivilegeDataError {
344    pub fn to_error_message(&self) -> String {
345        match self {
346            GetAllDatabasesPrivilegeDataError::MySqlError(err) => format!("MySQL error: {}", err),
347        }
348    }
349}
350
351pub type ModifyDatabasePrivilegesOutput =
352    BTreeMap<(MySQLDatabase, MySQLUser), Result<(), ModifyDatabasePrivilegesError>>;
353#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
354pub enum ModifyDatabasePrivilegesError {
355    DatabaseSanitizationError(NameValidationError),
356    DatabaseOwnershipError(OwnerValidationError),
357    UserSanitizationError(NameValidationError),
358    UserOwnershipError(OwnerValidationError),
359    DatabaseDoesNotExist,
360    DiffDoesNotApply(DiffDoesNotApplyError),
361    MySqlError(String),
362}
363
364#[allow(clippy::enum_variant_names)]
365#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
366pub enum DiffDoesNotApplyError {
367    RowAlreadyExists(MySQLDatabase, MySQLUser),
368    RowDoesNotExist(MySQLDatabase, MySQLUser),
369    RowPrivilegeChangeDoesNotApply(DatabasePrivilegeRowDiff, DatabasePrivilegeRow),
370}
371
372pub fn print_modify_database_privileges_output_status(output: &ModifyDatabasePrivilegesOutput) {
373    for ((database_name, username), result) in output {
374        match result {
375            Ok(()) => {
376                println!(
377                    "Privileges for user '{}' on database '{}' modified successfully.",
378                    username, database_name
379                );
380            }
381            Err(err) => {
382                println!("{}", err.to_error_message(database_name, username));
383                println!("Skipping...");
384            }
385        }
386        println!();
387    }
388}
389
390impl ModifyDatabasePrivilegesError {
391    pub fn to_error_message(&self, database_name: &MySQLDatabase, username: &MySQLUser) -> String {
392        match self {
393            ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => {
394                err.to_error_message(database_name, DbOrUser::Database)
395            }
396            ModifyDatabasePrivilegesError::DatabaseOwnershipError(err) => {
397                err.to_error_message(database_name, DbOrUser::Database)
398            }
399            ModifyDatabasePrivilegesError::UserSanitizationError(err) => {
400                err.to_error_message(username, DbOrUser::User)
401            }
402            ModifyDatabasePrivilegesError::UserOwnershipError(err) => {
403                err.to_error_message(username, DbOrUser::User)
404            }
405            ModifyDatabasePrivilegesError::DatabaseDoesNotExist => {
406                format!("Database '{}' does not exist.", database_name)
407            }
408            ModifyDatabasePrivilegesError::DiffDoesNotApply(diff) => {
409                format!(
410                    "Could not apply privilege change:\n{}",
411                    diff.to_error_message()
412                )
413            }
414            ModifyDatabasePrivilegesError::MySqlError(err) => {
415                format!("MySQL error: {}", err)
416            }
417        }
418    }
419}
420
421impl DiffDoesNotApplyError {
422    pub fn to_error_message(&self) -> String {
423        match self {
424            DiffDoesNotApplyError::RowAlreadyExists(database_name, username) => {
425                format!(
426                    "Privileges for user '{}' on database '{}' already exist.",
427                    username, database_name
428                )
429            }
430            DiffDoesNotApplyError::RowDoesNotExist(database_name, username) => {
431                format!(
432                    "Privileges for user '{}' on database '{}' do not exist.",
433                    username, database_name
434                )
435            }
436            DiffDoesNotApplyError::RowPrivilegeChangeDoesNotApply(diff, row) => {
437                format!(
438                    "Could not apply privilege change {:?} to row {:?}",
439                    diff, row
440                )
441            }
442        }
443    }
444}
445
446pub type CreateUsersOutput = BTreeMap<MySQLUser, Result<(), CreateUserError>>;
447#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
448pub enum CreateUserError {
449    SanitizationError(NameValidationError),
450    OwnershipError(OwnerValidationError),
451    UserAlreadyExists,
452    MySqlError(String),
453}
454
455pub fn print_create_users_output_status(output: &CreateUsersOutput) {
456    for (username, result) in output {
457        match result {
458            Ok(()) => {
459                println!("User '{}' created successfully.", username);
460            }
461            Err(err) => {
462                println!("{}", err.to_error_message(username));
463                println!("Skipping...");
464            }
465        }
466        println!();
467    }
468}
469
470pub fn print_create_users_output_status_json(output: &CreateUsersOutput) {
471    let value = output
472        .iter()
473        .map(|(name, result)| match result {
474            Ok(()) => (name.to_string(), json!({ "status": "success" })),
475            Err(err) => (
476                name.to_string(),
477                json!({
478                  "status": "error",
479                  "error": err.to_error_message(name),
480                }),
481            ),
482        })
483        .collect::<serde_json::Map<_, _>>();
484    println!(
485        "{}",
486        serde_json::to_string_pretty(&value)
487            .unwrap_or("Failed to serialize result to JSON".to_string())
488    );
489}
490
491impl CreateUserError {
492    pub fn to_error_message(&self, username: &MySQLUser) -> String {
493        match self {
494            CreateUserError::SanitizationError(err) => {
495                err.to_error_message(username, DbOrUser::User)
496            }
497            CreateUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
498            CreateUserError::UserAlreadyExists => {
499                format!("User '{}' already exists.", username)
500            }
501            CreateUserError::MySqlError(err) => {
502                format!("MySQL error: {}", err)
503            }
504        }
505    }
506}
507
508pub type DropUsersOutput = BTreeMap<MySQLUser, Result<(), DropUserError>>;
509#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
510pub enum DropUserError {
511    SanitizationError(NameValidationError),
512    OwnershipError(OwnerValidationError),
513    UserDoesNotExist,
514    MySqlError(String),
515}
516
517pub fn print_drop_users_output_status(output: &DropUsersOutput) {
518    for (username, result) in output {
519        match result {
520            Ok(()) => {
521                println!("User '{}' dropped successfully.", username);
522            }
523            Err(err) => {
524                println!("{}", err.to_error_message(username));
525                println!("Skipping...");
526            }
527        }
528        println!();
529    }
530}
531
532pub fn print_drop_users_output_status_json(output: &DropUsersOutput) {
533    let value = output
534        .iter()
535        .map(|(name, result)| match result {
536            Ok(()) => (name.to_string(), json!({ "status": "success" })),
537            Err(err) => (
538                name.to_string(),
539                json!({
540                  "status": "error",
541                  "error": err.to_error_message(name),
542                }),
543            ),
544        })
545        .collect::<serde_json::Map<_, _>>();
546    println!(
547        "{}",
548        serde_json::to_string_pretty(&value)
549            .unwrap_or("Failed to serialize result to JSON".to_string())
550    );
551}
552
553impl DropUserError {
554    pub fn to_error_message(&self, username: &MySQLUser) -> String {
555        match self {
556            DropUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
557            DropUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
558            DropUserError::UserDoesNotExist => {
559                format!("User '{}' does not exist.", username)
560            }
561            DropUserError::MySqlError(err) => {
562                format!("MySQL error: {}", err)
563            }
564        }
565    }
566}
567
568pub type SetPasswordOutput = Result<(), SetPasswordError>;
569#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
570pub enum SetPasswordError {
571    SanitizationError(NameValidationError),
572    OwnershipError(OwnerValidationError),
573    UserDoesNotExist,
574    MySqlError(String),
575}
576
577pub fn print_set_password_output_status(output: &SetPasswordOutput, username: &MySQLUser) {
578    match output {
579        Ok(()) => {
580            println!("Password for user '{}' set successfully.", username);
581        }
582        Err(err) => {
583            println!("{}", err.to_error_message(username));
584            println!("Skipping...");
585        }
586    }
587}
588
589impl SetPasswordError {
590    pub fn to_error_message(&self, username: &MySQLUser) -> String {
591        match self {
592            SetPasswordError::SanitizationError(err) => {
593                err.to_error_message(username, DbOrUser::User)
594            }
595            SetPasswordError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
596            SetPasswordError::UserDoesNotExist => {
597                format!("User '{}' does not exist.", username)
598            }
599            SetPasswordError::MySqlError(err) => {
600                format!("MySQL error: {}", err)
601            }
602        }
603    }
604}
605
606pub type LockUsersOutput = BTreeMap<MySQLUser, Result<(), LockUserError>>;
607#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
608pub enum LockUserError {
609    SanitizationError(NameValidationError),
610    OwnershipError(OwnerValidationError),
611    UserDoesNotExist,
612    UserIsAlreadyLocked,
613    MySqlError(String),
614}
615
616pub fn print_lock_users_output_status(output: &LockUsersOutput) {
617    for (username, result) in output {
618        match result {
619            Ok(()) => {
620                println!("User '{}' locked successfully.", username);
621            }
622            Err(err) => {
623                println!("{}", err.to_error_message(username));
624                println!("Skipping...");
625            }
626        }
627        println!();
628    }
629}
630
631pub fn print_lock_users_output_status_json(output: &LockUsersOutput) {
632    let value = output
633        .iter()
634        .map(|(name, result)| match result {
635            Ok(()) => (name.to_string(), json!({ "status": "success" })),
636            Err(err) => (
637                name.to_string(),
638                json!({
639                  "status": "error",
640                  "error": err.to_error_message(name),
641                }),
642            ),
643        })
644        .collect::<serde_json::Map<_, _>>();
645    println!(
646        "{}",
647        serde_json::to_string_pretty(&value)
648            .unwrap_or("Failed to serialize result to JSON".to_string())
649    );
650}
651
652impl LockUserError {
653    pub fn to_error_message(&self, username: &MySQLUser) -> String {
654        match self {
655            LockUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
656            LockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
657            LockUserError::UserDoesNotExist => {
658                format!("User '{}' does not exist.", username)
659            }
660            LockUserError::UserIsAlreadyLocked => {
661                format!("User '{}' is already locked.", username)
662            }
663            LockUserError::MySqlError(err) => {
664                format!("MySQL error: {}", err)
665            }
666        }
667    }
668}
669
670pub type UnlockUsersOutput = BTreeMap<MySQLUser, Result<(), UnlockUserError>>;
671#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
672pub enum UnlockUserError {
673    SanitizationError(NameValidationError),
674    OwnershipError(OwnerValidationError),
675    UserDoesNotExist,
676    UserIsAlreadyUnlocked,
677    MySqlError(String),
678}
679
680pub fn print_unlock_users_output_status(output: &UnlockUsersOutput) {
681    for (username, result) in output {
682        match result {
683            Ok(()) => {
684                println!("User '{}' unlocked successfully.", username);
685            }
686            Err(err) => {
687                println!("{}", err.to_error_message(username));
688                println!("Skipping...");
689            }
690        }
691        println!();
692    }
693}
694
695pub fn print_unlock_users_output_status_json(output: &UnlockUsersOutput) {
696    let value = output
697        .iter()
698        .map(|(name, result)| match result {
699            Ok(()) => (name.to_string(), json!({ "status": "success" })),
700            Err(err) => (
701                name.to_string(),
702                json!({
703                  "status": "error",
704                  "error": err.to_error_message(name),
705                }),
706            ),
707        })
708        .collect::<serde_json::Map<_, _>>();
709    println!(
710        "{}",
711        serde_json::to_string_pretty(&value)
712            .unwrap_or("Failed to serialize result to JSON".to_string())
713    );
714}
715
716impl UnlockUserError {
717    pub fn to_error_message(&self, username: &MySQLUser) -> String {
718        match self {
719            UnlockUserError::SanitizationError(err) => {
720                err.to_error_message(username, DbOrUser::User)
721            }
722            UnlockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
723            UnlockUserError::UserDoesNotExist => {
724                format!("User '{}' does not exist.", username)
725            }
726            UnlockUserError::UserIsAlreadyUnlocked => {
727                format!("User '{}' is already unlocked.", username)
728            }
729            UnlockUserError::MySqlError(err) => {
730                format!("MySQL error: {}", err)
731            }
732        }
733    }
734}
735
736pub type ListUsersOutput = BTreeMap<MySQLUser, Result<DatabaseUser, ListUsersError>>;
737#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
738pub enum ListUsersError {
739    SanitizationError(NameValidationError),
740    OwnershipError(OwnerValidationError),
741    UserDoesNotExist,
742    MySqlError(String),
743}
744
745impl ListUsersError {
746    pub fn to_error_message(&self, username: &MySQLUser) -> String {
747        match self {
748            ListUsersError::SanitizationError(err) => {
749                err.to_error_message(username, DbOrUser::User)
750            }
751            ListUsersError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
752            ListUsersError::UserDoesNotExist => {
753                format!("User '{}' does not exist.", username)
754            }
755            ListUsersError::MySqlError(err) => {
756                format!("MySQL error: {}", err)
757            }
758        }
759    }
760}
761
762pub type ListAllUsersOutput = Result<Vec<DatabaseUser>, ListAllUsersError>;
763#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
764pub enum ListAllUsersError {
765    MySqlError(String),
766}
767
768impl ListAllUsersError {
769    pub fn to_error_message(&self) -> String {
770        match self {
771            ListAllUsersError::MySqlError(err) => format!("MySQL error: {}", err),
772        }
773    }
774}