use std::{fmt::Display, sync::Arc}; use inquire::{min_length, prompt_text, Confirm, Password, Select, Text}; use sea_orm::{ActiveModelTrait, ActiveValue::{NotSet, Set}, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter}; use crate::{entities::{owner, prelude::User, user::{self, ActiveModel}}, utils::auth::hash_password}; #[derive(Debug, Copy, Clone)] enum Action { Add, Delete, Update } impl Action { const VARIANTS: &'static [Action] = &[ Self::Add, Self::Delete, Self::Update, ]; } impl Display for Action { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{self:?}") } } #[derive(Debug, Copy, Clone)] enum Update { Username, Password } impl Update { const VARIANTS: &'static [Update] = &[ Self::Username, Self::Password, ]; } impl Display for Update { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{self:?}") } } pub async fn manage_users(db: Arc) { loop { match Select::new("User Manager (ESC to quit)", Action::VARIANTS.to_vec()).prompt_skippable() { Ok(Some(action)) => { match action { Action::Add => { let username = Text::new("Username").with_validator(min_length!(3)).prompt().unwrap(); if User::find().filter(user::Column::Username.eq(username.clone())).one(db.as_ref()).await.is_ok_and(|r| r.is_some()) { println!("Username already in use !"); } else { let password = Password::new("Password") .with_validator(min_length!(10)) .with_display_mode(inquire::PasswordDisplayMode::Masked) .prompt().unwrap(); let new_user = user::ActiveModel { id: NotSet, username: Set(username), hashed_password: Set(hash_password(password)) }; let res = new_user.clone().insert(db.as_ref()).await.unwrap(); println!("Create corresponding owner:"); let first_name = prompt_text("First Name: ").unwrap(); let last_name = prompt_text("Last Name: ").unwrap(); let contact = prompt_text("Contact: ").unwrap(); let new_owner = owner::ActiveModel { id: NotSet, user_id: Set(res.id), first_name: Set(first_name), last_name: Set(last_name), contact: Set(contact), user: Set(true) }; let _ = new_owner.insert(db.as_ref()).await.unwrap(); } }, Action::Delete => { match select_user(db.clone()).await { Some(user) => { let username = user.username.clone(); match Confirm::new(format!("Delete {username} ?").as_ref()) .with_default(false) .with_help_message("The user and all associated data will *permanently* be deleted") .prompt() { Ok(true) => { let _ = user.delete(db.as_ref()).await; println!("{username} has been permanently deleted") }, Ok(false) | Err(_) => println!("Cancelled deletion of {username}"), } }, None => println!("Could not find user") } }, Action::Update => { match select_user(db.clone()).await { Some(user) => { match Select::new(format!("Editing {}", user.username).as_ref(), Update::VARIANTS.to_vec()).prompt() { Ok(v) => { let mut updated_user = ActiveModel::from(user); match v { Update::Username => { updated_user.username = Set(Text::new("New username") .with_initial_value(updated_user.username.unwrap().as_ref()) .prompt().unwrap() ) }, Update::Password => { match Password::new("Password") .with_validator(min_length!(10)) .with_display_mode(inquire::PasswordDisplayMode::Masked) .prompt() { Ok(new_password) => updated_user.hashed_password = Set(hash_password(new_password)), Err(_) => println!("Cancelled user update") } } } let _ = updated_user.save(db.as_ref()).await; }, Err(_) => {} } }, None => println!("Could not find user") } } } }, Ok(None) | Err(_) => { break; } } } } async fn select_user(db: Arc) -> Option { let users = User::find().all(db.as_ref()).await.unwrap(); if users.is_empty() { return None; } match Select::new("Select a user", users.iter().map(|u| u.username.clone()).collect()).prompt() { Ok(selection) => User::find().filter(user::Column::Username.eq(selection)).one(db.as_ref()).await.unwrap(), Err(_) => None } }