shared/domain/role/
model.rs1use chrono::{DateTime, Utc};
2use mongodb::bson;
3use serde::{Deserialize, Serialize};
4use sqlx::FromRow;
5use utoipa::ToSchema;
6
7use crate::domain::query::IntoBsonDocument;
8
9use super::super::serde::{deserialize_datetime, deserialize_object_id_as_string};
10
11#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize, Serialize, FromRow, ToSchema)]
12#[schema(example = json!({"id": Some(String::default()), "name": String::default(), "created_at": "2026-02-19T22:42:23.467Z"}))]
13pub struct Role {
14 #[serde(
15 rename = "_id",
16 default,
17 skip_serializing_if = "Option::is_none",
18 deserialize_with = "deserialize_object_id_as_string"
19 )]
20 pub id: Option<String>,
21
22 pub name: String,
23
24 #[sqlx(rename = "createdAt")]
25 #[serde(rename = "createdAt", deserialize_with = "deserialize_datetime")]
26 pub created_at: DateTime<Utc>,
27}
28
29impl Role {
30 pub fn id(&self) -> Result<&str, crate::error::CoreError> {
31 self.id.as_deref().ok_or_else(|| {
32 tracing::error!(
33 error_code = "ValidationError::Malformed",
34 "Unexpected null/missing data"
35 );
36 crate::error::CoreError::Validation(crate::error::ValidationError::Malformed {
37 field: crate::error::CredentialField::ObjectId,
38 })
39 })
40 }
41}
42
43impl Role {
44 pub fn new(name: &str) -> Self {
45 Self {
46 id: None,
47 name: name.into(),
48 created_at: Utc::now(),
49 }
50 }
51}
52
53impl IntoBsonDocument for Role {
54 fn into_bson_document(self) -> Result<bson::Document, bson::ser::Error> {
55 let mut doc = bson::to_document(&self)?;
56
57 for key in &["createdAt"] {
58 if let Some(bson::Bson::String(s)) = doc.get(*key).cloned()
59 && let Ok(dt) = DateTime::parse_from_rfc3339(&s)
60 {
61 doc.insert(
62 *key,
63 bson::Bson::DateTime(bson::DateTime::from_millis(dt.timestamp_millis())),
64 );
65 }
66 }
67
68 Ok(doc)
69 }
70}