use crate::validation::{Validate, ValidationError};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct UserWithRolesResponse {
pub id: Uuid,
pub email: String,
pub first_name: String,
pub last_name: String,
pub is_active: bool,
pub roles: Vec<UserRoleSummary>,
}
#[derive(Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct UserRoleSummary {
pub id: Uuid,
pub name: String,
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct UpdateUserRolesRequest {
pub role_ids: Vec<Uuid>,
}
impl Validate for UpdateUserRolesRequest {
fn validate(&self) -> Result<(), ValidationError> {
if self.role_ids.is_empty() {
return Err(ValidationError {
field: "role_ids",
message: "at least one role must be assigned".to_string(),
});
}
if self.role_ids.len() > 20 {
return Err(ValidationError {
field: "role_ids",
message: "cannot assign more than 20 roles".to_string(),
});
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct UpdateUserActiveRequest {
pub is_active: bool,
}
impl Validate for UpdateUserActiveRequest {
fn validate(&self) -> Result<(), ValidationError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
#![expect(
clippy::assertions_on_result_states,
reason = "test assertions — is_ok/is_err provides readable failure messages"
)]
use super::*;
#[test]
fn update_user_active_validate_is_ok() {
assert!(
UpdateUserActiveRequest { is_active: false }
.validate()
.is_ok()
);
}
}