Wrap database users and database names in newtypes
Also, use less cloning where possible
This commit was merged in pull request #81.
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
fmt::{Display, Formatter},
|
||||
ops::{Deref, DerefMut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::UnixStream;
|
||||
@@ -31,21 +36,107 @@ pub fn create_client_to_server_message_stream(socket: UnixStream) -> ClientToSer
|
||||
tokio_serde::Framed::new(length_delimited, Bincode::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct MySQLUser(String);
|
||||
|
||||
impl FromStr for MySQLUser {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(MySQLUser(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MySQLUser {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MySQLUser {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MySQLUser {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MySQLUser {
|
||||
fn from(s: &str) -> Self {
|
||||
MySQLUser(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MySQLUser {
|
||||
fn from(s: String) -> Self {
|
||||
MySQLUser(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct MySQLDatabase(String);
|
||||
|
||||
impl FromStr for MySQLDatabase {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(MySQLDatabase(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MySQLDatabase {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MySQLDatabase {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MySQLDatabase {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MySQLDatabase {
|
||||
fn from(s: &str) -> Self {
|
||||
MySQLDatabase(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MySQLDatabase {
|
||||
fn from(s: String) -> Self {
|
||||
MySQLDatabase(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Request {
|
||||
CreateDatabases(Vec<String>),
|
||||
DropDatabases(Vec<String>),
|
||||
ListDatabases(Option<Vec<String>>),
|
||||
ListPrivileges(Option<Vec<String>>),
|
||||
CreateDatabases(Vec<MySQLDatabase>),
|
||||
DropDatabases(Vec<MySQLDatabase>),
|
||||
ListDatabases(Option<Vec<MySQLDatabase>>),
|
||||
ListPrivileges(Option<Vec<MySQLDatabase>>),
|
||||
ModifyPrivileges(BTreeSet<DatabasePrivilegesDiff>),
|
||||
|
||||
CreateUsers(Vec<String>),
|
||||
DropUsers(Vec<String>),
|
||||
PasswdUser(String, String),
|
||||
ListUsers(Option<Vec<String>>),
|
||||
LockUsers(Vec<String>),
|
||||
UnlockUsers(Vec<String>),
|
||||
CreateUsers(Vec<MySQLUser>),
|
||||
DropUsers(Vec<MySQLUser>),
|
||||
PasswdUser(MySQLUser, String),
|
||||
ListUsers(Option<Vec<MySQLUser>>),
|
||||
LockUsers(Vec<MySQLUser>),
|
||||
UnlockUsers(Vec<MySQLUser>),
|
||||
|
||||
// Commit,
|
||||
Exit,
|
||||
|
||||
@@ -13,6 +13,8 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{MySQLDatabase, MySQLUser};
|
||||
|
||||
/// This enum is used to differentiate between database and user operations.
|
||||
/// Their output are very similar, but there are slight differences in the words used.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@@ -22,17 +24,17 @@ pub enum DbOrUser {
|
||||
}
|
||||
|
||||
impl DbOrUser {
|
||||
pub fn lowercased(&self) -> String {
|
||||
pub fn lowercased(&self) -> &'static str {
|
||||
match self {
|
||||
DbOrUser::Database => "database".to_string(),
|
||||
DbOrUser::User => "user".to_string(),
|
||||
DbOrUser::Database => "database",
|
||||
DbOrUser::User => "user",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capitalized(&self) -> String {
|
||||
pub fn capitalized(&self) -> &'static str {
|
||||
match self {
|
||||
DbOrUser::Database => "Database".to_string(),
|
||||
DbOrUser::User => "User".to_string(),
|
||||
DbOrUser::Database => "Database",
|
||||
DbOrUser::User => "User",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,6 +75,11 @@ impl OwnerValidationError {
|
||||
pub fn to_error_message(self, name: &str, db_or_user: DbOrUser) -> String {
|
||||
let user = UnixUser::from_enviroment();
|
||||
|
||||
let UnixUser { username, groups } = user.unwrap_or(UnixUser {
|
||||
username: "???".to_string(),
|
||||
groups: vec![],
|
||||
});
|
||||
|
||||
match self {
|
||||
OwnerValidationError::NoMatch => format!(
|
||||
indoc! {r#"
|
||||
@@ -88,11 +95,8 @@ impl OwnerValidationError {
|
||||
name,
|
||||
db_or_user.lowercased(),
|
||||
db_or_user.lowercased(),
|
||||
user.as_ref()
|
||||
.map(|u| u.username.clone())
|
||||
.unwrap_or("???".to_string()),
|
||||
user.map(|u| u.groups)
|
||||
.unwrap_or_default()
|
||||
username,
|
||||
groups
|
||||
.iter()
|
||||
.map(|g| format!(" - {}", g))
|
||||
.sorted()
|
||||
@@ -118,7 +122,7 @@ pub enum OwnerValidationError {
|
||||
StringEmpty,
|
||||
}
|
||||
|
||||
pub type CreateDatabasesOutput = BTreeMap<String, Result<(), CreateDatabaseError>>;
|
||||
pub type CreateDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), CreateDatabaseError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum CreateDatabaseError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -146,9 +150,9 @@ pub fn print_create_databases_output_status_json(output: &CreateDatabasesOutput)
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -164,7 +168,7 @@ pub fn print_create_databases_output_status_json(output: &CreateDatabasesOutput)
|
||||
}
|
||||
|
||||
impl CreateDatabaseError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||
match self {
|
||||
CreateDatabaseError::SanitizationError(err) => {
|
||||
err.to_error_message(database_name, DbOrUser::Database)
|
||||
@@ -182,7 +186,7 @@ impl CreateDatabaseError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type DropDatabasesOutput = BTreeMap<String, Result<(), DropDatabaseError>>;
|
||||
pub type DropDatabasesOutput = BTreeMap<MySQLDatabase, Result<(), DropDatabaseError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DropDatabaseError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -195,7 +199,10 @@ pub fn print_drop_databases_output_status(output: &DropDatabasesOutput) {
|
||||
for (database_name, result) in output {
|
||||
match result {
|
||||
Ok(()) => {
|
||||
println!("Database '{}' dropped successfully.", database_name);
|
||||
println!(
|
||||
"Database '{}' dropped successfully.",
|
||||
database_name.as_str()
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err.to_error_message(database_name));
|
||||
@@ -210,9 +217,9 @@ pub fn print_drop_databases_output_status_json(output: &DropDatabasesOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -228,7 +235,7 @@ pub fn print_drop_databases_output_status_json(output: &DropDatabasesOutput) {
|
||||
}
|
||||
|
||||
impl DropDatabaseError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||
match self {
|
||||
DropDatabaseError::SanitizationError(err) => {
|
||||
err.to_error_message(database_name, DbOrUser::Database)
|
||||
@@ -246,7 +253,7 @@ impl DropDatabaseError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type ListDatabasesOutput = BTreeMap<String, Result<DatabaseRow, ListDatabasesError>>;
|
||||
pub type ListDatabasesOutput = BTreeMap<MySQLDatabase, Result<DatabaseRow, ListDatabasesError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ListDatabasesError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -256,7 +263,7 @@ pub enum ListDatabasesError {
|
||||
}
|
||||
|
||||
impl ListDatabasesError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||
match self {
|
||||
ListDatabasesError::SanitizationError(err) => {
|
||||
err.to_error_message(database_name, DbOrUser::Database)
|
||||
@@ -293,7 +300,7 @@ impl ListAllDatabasesError {
|
||||
// no need to index by database name.
|
||||
|
||||
pub type GetDatabasesPrivilegeData =
|
||||
BTreeMap<String, Result<Vec<DatabasePrivilegeRow>, GetDatabasesPrivilegeDataError>>;
|
||||
BTreeMap<MySQLDatabase, Result<Vec<DatabasePrivilegeRow>, GetDatabasesPrivilegeDataError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum GetDatabasesPrivilegeDataError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -303,7 +310,7 @@ pub enum GetDatabasesPrivilegeDataError {
|
||||
}
|
||||
|
||||
impl GetDatabasesPrivilegeDataError {
|
||||
pub fn to_error_message(&self, database_name: &str) -> String {
|
||||
pub fn to_error_message(&self, database_name: &MySQLDatabase) -> String {
|
||||
match self {
|
||||
GetDatabasesPrivilegeDataError::SanitizationError(err) => {
|
||||
err.to_error_message(database_name, DbOrUser::Database)
|
||||
@@ -337,7 +344,7 @@ impl GetAllDatabasesPrivilegeDataError {
|
||||
}
|
||||
|
||||
pub type ModifyDatabasePrivilegesOutput =
|
||||
BTreeMap<(String, String), Result<(), ModifyDatabasePrivilegesError>>;
|
||||
BTreeMap<(MySQLDatabase, MySQLUser), Result<(), ModifyDatabasePrivilegesError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ModifyDatabasePrivilegesError {
|
||||
DatabaseSanitizationError(NameValidationError),
|
||||
@@ -352,8 +359,8 @@ pub enum ModifyDatabasePrivilegesError {
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DiffDoesNotApplyError {
|
||||
RowAlreadyExists(String, String),
|
||||
RowDoesNotExist(String, String),
|
||||
RowAlreadyExists(MySQLDatabase, MySQLUser),
|
||||
RowDoesNotExist(MySQLDatabase, MySQLUser),
|
||||
RowPrivilegeChangeDoesNotApply(DatabasePrivilegeRowDiff, DatabasePrivilegeRow),
|
||||
}
|
||||
|
||||
@@ -376,7 +383,7 @@ pub fn print_modify_database_privileges_output_status(output: &ModifyDatabasePri
|
||||
}
|
||||
|
||||
impl ModifyDatabasePrivilegesError {
|
||||
pub fn to_error_message(&self, database_name: &str, username: &str) -> String {
|
||||
pub fn to_error_message(&self, database_name: &MySQLDatabase, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
ModifyDatabasePrivilegesError::DatabaseSanitizationError(err) => {
|
||||
err.to_error_message(database_name, DbOrUser::Database)
|
||||
@@ -431,7 +438,7 @@ impl DiffDoesNotApplyError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type CreateUsersOutput = BTreeMap<String, Result<(), CreateUserError>>;
|
||||
pub type CreateUsersOutput = BTreeMap<MySQLUser, Result<(), CreateUserError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum CreateUserError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -459,9 +466,9 @@ pub fn print_create_users_output_status_json(output: &CreateUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -477,7 +484,7 @@ pub fn print_create_users_output_status_json(output: &CreateUsersOutput) {
|
||||
}
|
||||
|
||||
impl CreateUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
CreateUserError::SanitizationError(err) => {
|
||||
err.to_error_message(username, DbOrUser::User)
|
||||
@@ -493,7 +500,7 @@ impl CreateUserError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type DropUsersOutput = BTreeMap<String, Result<(), DropUserError>>;
|
||||
pub type DropUsersOutput = BTreeMap<MySQLUser, Result<(), DropUserError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DropUserError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -521,9 +528,9 @@ pub fn print_drop_users_output_status_json(output: &DropUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -539,7 +546,7 @@ pub fn print_drop_users_output_status_json(output: &DropUsersOutput) {
|
||||
}
|
||||
|
||||
impl DropUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
DropUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
||||
DropUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||
@@ -562,7 +569,7 @@ pub enum SetPasswordError {
|
||||
MySqlError(String),
|
||||
}
|
||||
|
||||
pub fn print_set_password_output_status(output: &SetPasswordOutput, username: &str) {
|
||||
pub fn print_set_password_output_status(output: &SetPasswordOutput, username: &MySQLUser) {
|
||||
match output {
|
||||
Ok(()) => {
|
||||
println!("Password for user '{}' set successfully.", username);
|
||||
@@ -575,7 +582,7 @@ pub fn print_set_password_output_status(output: &SetPasswordOutput, username: &s
|
||||
}
|
||||
|
||||
impl SetPasswordError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
SetPasswordError::SanitizationError(err) => {
|
||||
err.to_error_message(username, DbOrUser::User)
|
||||
@@ -591,7 +598,7 @@ impl SetPasswordError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type LockUsersOutput = BTreeMap<String, Result<(), LockUserError>>;
|
||||
pub type LockUsersOutput = BTreeMap<MySQLUser, Result<(), LockUserError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum LockUserError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -620,9 +627,9 @@ pub fn print_lock_users_output_status_json(output: &LockUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -638,7 +645,7 @@ pub fn print_lock_users_output_status_json(output: &LockUsersOutput) {
|
||||
}
|
||||
|
||||
impl LockUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
LockUserError::SanitizationError(err) => err.to_error_message(username, DbOrUser::User),
|
||||
LockUserError::OwnershipError(err) => err.to_error_message(username, DbOrUser::User),
|
||||
@@ -655,7 +662,7 @@ impl LockUserError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type UnlockUsersOutput = BTreeMap<String, Result<(), UnlockUserError>>;
|
||||
pub type UnlockUsersOutput = BTreeMap<MySQLUser, Result<(), UnlockUserError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum UnlockUserError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -684,9 +691,9 @@ pub fn print_unlock_users_output_status_json(output: &UnlockUsersOutput) {
|
||||
let value = output
|
||||
.iter()
|
||||
.map(|(name, result)| match result {
|
||||
Ok(()) => (name.to_owned(), json!({ "status": "success" })),
|
||||
Ok(()) => (name.to_string(), json!({ "status": "success" })),
|
||||
Err(err) => (
|
||||
name.clone(),
|
||||
name.to_string(),
|
||||
json!({
|
||||
"status": "error",
|
||||
"error": err.to_error_message(name),
|
||||
@@ -702,7 +709,7 @@ pub fn print_unlock_users_output_status_json(output: &UnlockUsersOutput) {
|
||||
}
|
||||
|
||||
impl UnlockUserError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
UnlockUserError::SanitizationError(err) => {
|
||||
err.to_error_message(username, DbOrUser::User)
|
||||
@@ -721,7 +728,7 @@ impl UnlockUserError {
|
||||
}
|
||||
}
|
||||
|
||||
pub type ListUsersOutput = BTreeMap<String, Result<DatabaseUser, ListUsersError>>;
|
||||
pub type ListUsersOutput = BTreeMap<MySQLUser, Result<DatabaseUser, ListUsersError>>;
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ListUsersError {
|
||||
SanitizationError(NameValidationError),
|
||||
@@ -731,7 +738,7 @@ pub enum ListUsersError {
|
||||
}
|
||||
|
||||
impl ListUsersError {
|
||||
pub fn to_error_message(&self, username: &str) -> String {
|
||||
pub fn to_error_message(&self, username: &MySQLUser) -> String {
|
||||
match self {
|
||||
ListUsersError::SanitizationError(err) => {
|
||||
err.to_error_message(username, DbOrUser::User)
|
||||
|
||||
Reference in New Issue
Block a user