Skip to main content

backbone_catalog/presentation/dto/
attribute_dto.rs

1//! Attribute DTOs (Data Transfer Objects)
2//!
3//! Generated by metaphor-schema. Do not edit manually.
4//!
5//! DTOs provide a clean separation between domain entities and API
6//! representations, with validation and OpenAPI documentation support.
7
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10use chrono::{DateTime, Utc};
11
12#[cfg(feature = "openapi")]
13#[cfg(feature = "openapi")]
14use utoipa::ToSchema;
15
16#[cfg(feature = "validation")]
17use validator::Validate;
18
19use crate::domain::entity::Attribute;
20use crate::domain::entity::AuditMetadata;
21use crate::domain::entity::AttributeType;
22use crate::domain::entity::CatalogStatus;
23
24// =============================================================================
25// Create DTO
26// =============================================================================
27
28/// Request DTO for creating a new Attribute
29///
30/// Used in POST requests to create new entities.
31/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
32#[derive(Debug, Clone, Deserialize)]
33#[cfg_attr(feature = "openapi", derive(ToSchema))]
34#[cfg_attr(feature = "validation", derive(Validate))]
35#[serde(rename_all = "camelCase")]
36pub struct CreateAttributeDto {
37    #[cfg_attr(feature = "validation", validate(length(max = 40)))]
38    #[cfg_attr(feature = "openapi", schema(example = "example"))]
39    pub code: String,
40    #[cfg_attr(feature = "validation", validate(length(max = 100)))]
41    #[cfg_attr(feature = "openapi", schema(example = "example"))]
42    pub name: String,
43    #[serde(alias = "attribute_type")]
44    pub attribute_type: AttributeType,
45    pub status: CatalogStatus,
46}
47
48// =============================================================================
49// Update DTO
50// =============================================================================
51
52/// Request DTO for full update of a Attribute
53///
54/// Used in PUT requests for full entity replacement.
55/// All fields are required (except auto-generated ones).
56#[derive(Debug, Clone, Deserialize)]
57#[cfg_attr(feature = "openapi", derive(ToSchema))]
58#[cfg_attr(feature = "validation", derive(Validate))]
59#[serde(rename_all = "camelCase")]
60pub struct UpdateAttributeDto {
61    #[cfg_attr(feature = "validation", validate(length(max = 40)))]
62    #[cfg_attr(feature = "openapi", schema(example = "example"))]
63    pub code: String,
64    #[cfg_attr(feature = "validation", validate(length(max = 100)))]
65    #[cfg_attr(feature = "openapi", schema(example = "example"))]
66    pub name: String,
67    #[serde(alias = "attribute_type")]
68    pub attribute_type: AttributeType,
69    pub status: CatalogStatus,
70}
71
72// =============================================================================
73// Patch DTO
74// =============================================================================
75
76/// Request DTO for partial update of a Attribute
77///
78/// Used in PATCH requests for partial updates.
79/// All fields are optional - only provided fields will be updated.
80#[derive(Debug, Clone, Default, Deserialize)]
81#[cfg_attr(feature = "openapi", derive(ToSchema))]
82#[cfg_attr(feature = "validation", derive(Validate))]
83#[serde(rename_all = "camelCase")]
84pub struct PatchAttributeDto {
85    #[cfg_attr(feature = "validation", validate(length(max = 40)))]
86    #[cfg_attr(feature = "openapi", schema(example = "example"))]
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub code: Option<String>,
89    #[cfg_attr(feature = "validation", validate(length(max = 100)))]
90    #[cfg_attr(feature = "openapi", schema(example = "example"))]
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub name: Option<String>,
93    #[serde(skip_serializing_if = "Option::is_none", alias = "attribute_type")]
94    pub attribute_type: Option<AttributeType>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub status: Option<CatalogStatus>,
97}
98
99impl PatchAttributeDto {
100    /// Check if any field is set
101    pub fn has_changes(&self) -> bool {
102        self.code.is_some() || self.name.is_some() || self.attribute_type.is_some() || self.status.is_some()
103    }
104}
105
106// =============================================================================
107// Response DTO
108// =============================================================================
109
110/// Response DTO for Attribute entity
111///
112/// Used in API responses for single entity.
113/// Includes all fields including metadata.
114#[derive(Debug, Clone, Serialize)]
115#[cfg_attr(feature = "openapi", derive(ToSchema))]
116#[serde(rename_all = "camelCase")]
117pub struct AttributeResponseDto {
118    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
119    pub id: Uuid,
120    #[cfg_attr(feature = "openapi", schema(example = "example"))]
121    pub code: String,
122    #[cfg_attr(feature = "openapi", schema(example = "example"))]
123    pub name: String,
124    pub attribute_type: AttributeType,
125    pub status: CatalogStatus,
126    pub metadata: AuditMetadata,
127}
128
129// =============================================================================
130// List Response DTO
131// =============================================================================
132
133/// Paginated list response for Attribute entities
134///
135/// Used in API responses for list endpoints.
136/// Includes pagination metadata.
137#[derive(Debug, Clone, Serialize)]
138#[cfg_attr(feature = "openapi", derive(ToSchema))]
139#[serde(rename_all = "camelCase")]
140pub struct AttributeListResponseDto {
141    /// List of items
142    pub items: Vec<AttributeResponseDto>,
143    /// Total number of items (across all pages)
144    pub total: u64,
145    /// Current page number (1-indexed)
146    pub page: u32,
147    /// Number of items per page
148    pub per_page: u32,
149    /// Total number of pages
150    pub total_pages: u32,
151    /// Whether there is a next page
152    pub has_next: bool,
153    /// Whether there is a previous page
154    pub has_prev: bool,
155}
156
157impl AttributeListResponseDto {
158    /// Create a new list response from items and pagination info
159    pub fn new(items: Vec<AttributeResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
160        let total_pages = if per_page > 0 {
161            ((total as f64) / (per_page as f64)).ceil() as u32
162        } else {
163            0
164        };
165        Self {
166            items,
167            total,
168            page,
169            per_page,
170            total_pages,
171            has_next: page < total_pages,
172            has_prev: page > 1,
173        }
174    }
175}
176
177/// Summary DTO for list views (compact version)
178#[derive(Debug, Clone, Serialize)]
179#[cfg_attr(feature = "openapi", derive(ToSchema))]
180#[serde(rename_all = "camelCase")]
181pub struct AttributeSummaryDto {
182    pub id: Uuid,
183    pub code: String,
184    pub name: String,
185    pub attribute_type: AttributeType,
186    pub created_at: Option<DateTime<Utc>>,
187}
188
189// =============================================================================
190// Conversions
191// =============================================================================
192
193impl From<Attribute> for AttributeResponseDto {
194    fn from(entity: Attribute) -> Self {
195        Self {
196            id: entity.id,
197            code: entity.code,
198            name: entity.name,
199            attribute_type: entity.attribute_type,
200            status: entity.status,
201            metadata: entity.metadata,
202        }
203    }
204}
205
206impl From<Attribute> for AttributeSummaryDto {
207    fn from(entity: Attribute) -> Self {
208        let created_at = backbone_core::PersistentEntity::created_at(&entity);
209        Self {
210            id: entity.id,
211            code: entity.code,
212            name: entity.name,
213            attribute_type: entity.attribute_type,
214            created_at,
215        }
216    }
217}
218
219impl From<CreateAttributeDto> for Attribute {
220    fn from(dto: CreateAttributeDto) -> Self {
221        Self {
222            id: Uuid::new_v4(),
223            code: dto.code,
224            name: dto.name,
225            attribute_type: dto.attribute_type,
226            status: dto.status,
227            metadata: AuditMetadata::default(),
228        }
229    }
230}
231
232impl From<&Attribute> for AttributeResponseDto {
233    fn from(entity: &Attribute) -> Self {
234        Self {
235            id: entity.id.clone(),
236            code: entity.code.clone(),
237            name: entity.name.clone(),
238            attribute_type: entity.attribute_type.clone(),
239            status: entity.status.clone(),
240            metadata: entity.metadata.clone(),
241        }
242    }
243}
244
245impl backbone_core::FromCreateDto<CreateAttributeDto> for Attribute {
246    fn from_create_dto(dto: CreateAttributeDto) -> backbone_core::ServiceResult<Self> {
247        Ok(Attribute::from(dto))
248    }
249}
250
251impl backbone_core::ApplyUpdateDto<UpdateAttributeDto> for Attribute {
252    fn apply_update(mut self, dto: UpdateAttributeDto) -> backbone_core::ServiceResult<Self> {
253        self.code = dto.code;
254        self.name = dto.name;
255        self.attribute_type = dto.attribute_type;
256        self.status = dto.status;
257        Ok(self)
258    }
259}
260
261// =============================================================================
262// Custom DTOs
263// =============================================================================
264
265// <<< CUSTOM DTOs
266// Add custom DTOs specific to Attribute here.
267// This section will be preserved during regeneration.
268// >>> END CUSTOM DTOs