use crate::Extensions;
use crate::extensions::{IsActive, IsAdmin, IsAuthenticated};
#[derive(Clone, Debug, PartialEq, Eq)]
struct AuthStateMarker;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AuthState {
user_id: String,
is_authenticated: bool,
is_admin: bool,
is_active: bool,
_marker: AuthStateMarker,
}
impl AuthState {
pub fn authenticated(user_id: impl Into<String>, is_admin: bool, is_active: bool) -> Self {
Self {
user_id: user_id.into(),
is_authenticated: true,
is_admin,
is_active,
_marker: AuthStateMarker,
}
}
pub fn anonymous() -> Self {
Self {
user_id: String::new(),
is_authenticated: false,
is_admin: false,
is_active: false,
_marker: AuthStateMarker,
}
}
pub fn from_extensions(extensions: &Extensions) -> Option<Self> {
if let Some(state) = extensions.get::<AuthState>() {
return Some(state);
}
let user_id = extensions.get::<String>()?;
let is_authenticated = extensions
.get::<IsAuthenticated>()
.map(|v| v.0)
.unwrap_or(false);
let is_admin = extensions.get::<IsAdmin>().map(|v| v.0).unwrap_or(false);
let is_active = extensions.get::<IsActive>().map(|v| v.0).unwrap_or(false);
Some(Self {
user_id,
is_authenticated,
is_admin,
is_active,
_marker: AuthStateMarker,
})
}
pub fn user_id(&self) -> &str {
&self.user_id
}
pub fn is_authenticated(&self) -> bool {
self.is_authenticated
}
pub fn is_admin(&self) -> bool {
self.is_admin
}
pub fn is_active(&self) -> bool {
self.is_active
}
pub fn is_anonymous(&self) -> bool {
!self.is_authenticated
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[test]
fn test_authenticated() {
let state = AuthState::authenticated("user-123", true, true);
assert_eq!(state.user_id(), "user-123");
assert!(state.is_authenticated());
assert!(state.is_admin());
assert!(state.is_active());
}
#[test]
fn test_anonymous() {
let state = AuthState::anonymous();
assert!(state.user_id().is_empty());
assert!(!state.is_authenticated());
assert!(!state.is_admin());
assert!(!state.is_active());
}
#[rstest]
fn test_from_extensions_with_authstate_object() {
let extensions = Extensions::new();
let state = AuthState::authenticated("user-456", true, true);
extensions.insert(state.clone());
let result = AuthState::from_extensions(&extensions);
assert_eq!(result, Some(state));
let retrieved = result.unwrap();
assert_eq!(retrieved.user_id(), "user-456");
assert!(retrieved.is_authenticated());
assert!(retrieved.is_admin());
assert!(retrieved.is_active());
}
#[rstest]
fn test_from_extensions_with_individual_values() {
let extensions = Extensions::new();
extensions.insert("user-789".to_string());
extensions.insert(IsAuthenticated(true));
let result = AuthState::from_extensions(&extensions);
assert!(result.is_some());
let retrieved = result.unwrap();
assert_eq!(retrieved.user_id(), "user-789");
assert!(retrieved.is_authenticated());
assert!(!retrieved.is_admin());
assert!(!retrieved.is_active());
}
#[rstest]
fn test_from_extensions_empty() {
let extensions = Extensions::new();
let result = AuthState::from_extensions(&extensions);
assert_eq!(result, None);
}
#[rstest]
fn test_from_extensions_preserves_admin_and_active() {
let extensions = Extensions::new();
let state = AuthState::authenticated("admin-user", true, true);
extensions.insert(state);
let result = AuthState::from_extensions(&extensions);
let retrieved = result.unwrap();
assert_eq!(retrieved.user_id(), "admin-user");
assert!(retrieved.is_authenticated());
assert!(retrieved.is_admin());
assert!(retrieved.is_active());
}
}