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#[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 NoMatch,
125
126 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
303pub 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}