hyaenidae/accounts/src/forms/update_username.rs

131 lines
3.4 KiB
Rust

use crate::{to_account_page, Authenticated, Error, State};
use actix_web::{error::BlockingError, web, HttpResponse};
pub type UpdateUsernamePageArgs = (Authenticated, web::Data<State>);
pub type UpdateUsernameArgs = (
Authenticated,
web::Data<State>,
web::Form<UpdateUsernameForm>,
);
#[derive(Debug, serde::Deserialize)]
pub struct UpdateUsernameForm {
new_username: String,
password: String,
}
#[derive(Debug)]
pub struct UpdateUsernameState {
state: State,
form: Option<UpdateUsernameForm>,
error: Option<UpdateUsernameError>,
}
pub fn update_username_page((_, state): UpdateUsernamePageArgs) -> UpdateUsernameState {
UpdateUsernameState::empty(&state)
}
pub async fn update_username(
(authenticated, state, form): UpdateUsernameArgs,
) -> Result<Result<UpdateUsernameState, HttpResponse>, Error> {
let form = form.into_inner();
match try_update_username(authenticated, &form, &state).await? {
Ok(res) => Ok(Err(res)),
Err(e) => Ok(Ok(UpdateUsernameState::new_from_request(&state, form, e))),
}
}
impl UpdateUsernameState {
fn empty(state: &State) -> Self {
UpdateUsernameState {
state: state.clone(),
form: None,
error: None,
}
}
fn new_from_request(
state: &State,
form: UpdateUsernameForm,
error: UpdateUsernameError,
) -> Self {
UpdateUsernameState {
state: state.clone(),
form: Some(form),
error: Some(error),
}
}
pub fn update_username_path(&self) -> String {
self.state.pages.update_username_path()
}
pub fn username(&self) -> Option<String> {
self.form.as_ref().map(|form| form.new_username.clone())
}
pub fn username_error(&self) -> Option<String> {
self.error.as_ref().and_then(|e| e.username())
}
pub fn password(&self) -> Option<String> {
self.form.as_ref().map(|form| form.password.clone())
}
pub fn password_error(&self) -> Option<String> {
self.error.as_ref().and_then(|e| e.password())
}
}
#[derive(Debug)]
enum UpdateUsernameError {
UsernameTaken,
Invalid,
}
impl UpdateUsernameError {
fn username(&self) -> Option<String> {
match self {
UpdateUsernameError::UsernameTaken => Some("Username is already in use".to_owned()),
_ => None,
}
}
fn password(&self) -> Option<String> {
match self {
UpdateUsernameError::Invalid => Some("Incorrect password supplied".to_owned()),
_ => None,
}
}
}
async fn try_update_username(
authenticated: Authenticated,
form: &UpdateUsernameForm,
state: &State,
) -> Result<Result<HttpResponse, UpdateUsernameError>, Error> {
let res = state
.user_store
.exec(crate::store::UpdateUsername {
user: authenticated.user(),
new_username: form.new_username.clone(),
password: form.password.clone(),
})
.await;
match res {
Ok(_) => (),
Err(BlockingError::Error(crate::store::StoreError::AuthenticationFailed)) => {
return Ok(Err(UpdateUsernameError::Invalid));
}
Err(BlockingError::Error(crate::store::StoreError::InUse)) => {
return Ok(Err(UpdateUsernameError::UsernameTaken));
}
Err(e) => return Err(e.into()),
};
Ok(Ok(to_account_page(state)))
}