use crate::{to_cookie_page, to_home, AcceptedCookies, Authenticated, Error, State}; use actix_session::Session; use actix_web::{error::BlockingError, web, HttpResponse}; pub type RegisterPageArgs = ( Option, Option, web::Data, ); pub type RegisterArgs = ( Option, Option, web::Data, Session, web::Form, ); #[derive(Debug, serde::Deserialize)] pub struct RegisterForm { username: String, password: String, password_confirmation: String, } #[derive(Debug)] pub struct RegisterState { state: State, form: Option, error: Option, pub(crate) dark: bool, } pub fn register_page( (cookies, authenticated, state): RegisterPageArgs, ) -> Result { if cookies.is_none() { return Err(to_cookie_page(&state)); } if authenticated.is_some() { return Err(to_home(&state)); } Ok(RegisterState::new_empty(&state)) } pub async fn register( (cookies, authenticated, state, session, form): RegisterArgs, ) -> Result, Error> { if cookies.is_none() { return Ok(Err(to_cookie_page(&state))); } if authenticated.is_some() { return Ok(Err(to_home(&state))); } let form = form.into_inner(); match try_register(session, &form, &state).await? { Ok(res) => Ok(Err(res)), Err(e) => Ok(Ok(RegisterState::new_from_request(&state, form, e))), } } impl RegisterState { fn new_empty(state: &State) -> Self { RegisterState { state: state.clone(), form: None, error: None, dark: false, } } fn new_from_request(state: &State, form: RegisterForm, error: RegisterError) -> Self { RegisterState { state: state.clone(), form: Some(form), error: Some(error), dark: false, } } pub fn dark(&mut self, dark: bool) { self.dark = dark; } pub(crate) fn register_path(&self) -> String { self.state.pages.register_path() } pub(crate) fn login_path(&self) -> String { self.state.pages.login_path() } pub(crate) fn home_path(&self) -> String { self.state.pages.home_path() } pub(crate) fn username(&self) -> Option { self.form.as_ref().map(|form| form.username.clone()) } pub(crate) fn username_error(&self) -> Option { self.error.as_ref().and_then(|e| e.username()) } pub(crate) fn password(&self) -> Option { self.form.as_ref().map(|form| form.password.clone()) } pub(crate) fn confirmation(&self) -> Option { self.form .as_ref() .map(|form| form.password_confirmation.clone()) } pub(crate) fn confirmation_error(&self) -> Option { self.error.as_ref().and_then(|e| e.confirmation()) } } #[derive(Debug)] enum RegisterError { UsernameTaken, PasswordMatch, } impl RegisterError { fn username(&self) -> Option { match self { RegisterError::UsernameTaken => Some("Username is already taken".to_owned()), _ => None, } } fn confirmation(&self) -> Option { match self { RegisterError::PasswordMatch => Some("Passwords do not match".to_owned()), _ => None, } } } async fn try_register( session: Session, form: &RegisterForm, state: &State, ) -> Result, Error> { if form.password != form.password_confirmation { return Ok(Err(RegisterError::PasswordMatch)); } let res = state .user_store .exec(crate::store::Register { username: form.username.clone(), password: form.password.clone(), }) .await; let user = match res { Ok(user) => user, Err(BlockingError::Error(crate::store::StoreError::InUse)) => { return Ok(Err(RegisterError::UsernameTaken)) } Err(e) => return Err(e.into()), }; crate::extractors::UserData::set_data(user.id(), &session)?; Ok(Ok(to_home(state))) }