Skip to main content

backbone_bucket/presentation/dto/
content_hash_dto.rs

1//! ContentHash 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 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
16use crate::domain::entity::ContentHash;
17use crate::domain::entity::AuditMetadata;
18use crate::domain::entity::StorageBackend;
19
20// =============================================================================
21// Create DTO
22// =============================================================================
23
24/// Request DTO for creating a new ContentHash
25///
26/// Used in POST requests to create new entities.
27/// Excludes auto-generated fields (id, created_at, updated_at, deleted_at).
28#[derive(Debug, Clone, Deserialize)]
29#[cfg_attr(feature = "openapi", derive(ToSchema))]
30#[serde(rename_all = "camelCase")]
31pub struct CreateContentHashDto {
32    #[cfg_attr(feature = "openapi", schema(example = "example"))]
33    pub hash: String,
34    #[cfg_attr(feature = "openapi", schema(example = 42))]
35    #[serde(alias = "size_bytes")]
36    pub size_bytes: i64,
37    #[cfg_attr(feature = "openapi", schema(example = "example"))]
38    #[serde(alias = "storage_key")]
39    pub storage_key: String,
40    #[serde(alias = "storage_backend")]
41    pub storage_backend: StorageBackend,
42    #[cfg_attr(feature = "openapi", schema(example = 42))]
43    #[serde(alias = "reference_count")]
44    pub reference_count: i32,
45    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
46    #[serde(alias = "first_uploaded_at")]
47    pub first_uploaded_at: DateTime<Utc>,
48    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
49    #[serde(alias = "last_referenced_at")]
50    pub last_referenced_at: DateTime<Utc>,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub fingerprint: Option<String>,
53}
54
55// =============================================================================
56// Update DTO
57// =============================================================================
58
59/// Request DTO for full update of a ContentHash
60///
61/// Used in PUT requests for full entity replacement.
62/// All fields are required (except auto-generated ones).
63#[derive(Debug, Clone, Deserialize)]
64#[cfg_attr(feature = "openapi", derive(ToSchema))]
65#[serde(rename_all = "camelCase")]
66pub struct UpdateContentHashDto {
67    #[cfg_attr(feature = "openapi", schema(example = "example"))]
68    pub hash: String,
69    #[cfg_attr(feature = "openapi", schema(example = 42))]
70    #[serde(alias = "size_bytes")]
71    pub size_bytes: i64,
72    #[cfg_attr(feature = "openapi", schema(example = "example"))]
73    #[serde(alias = "storage_key")]
74    pub storage_key: String,
75    #[serde(alias = "storage_backend")]
76    pub storage_backend: StorageBackend,
77    #[cfg_attr(feature = "openapi", schema(example = 42))]
78    #[serde(alias = "reference_count")]
79    pub reference_count: i32,
80    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
81    #[serde(alias = "first_uploaded_at")]
82    pub first_uploaded_at: DateTime<Utc>,
83    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
84    #[serde(alias = "last_referenced_at")]
85    pub last_referenced_at: DateTime<Utc>,
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    pub fingerprint: Option<String>,
88}
89
90// =============================================================================
91// Patch DTO
92// =============================================================================
93
94/// Request DTO for partial update of a ContentHash
95///
96/// Used in PATCH requests for partial updates.
97/// All fields are optional - only provided fields will be updated.
98#[derive(Debug, Clone, Default, Deserialize)]
99#[cfg_attr(feature = "openapi", derive(ToSchema))]
100#[serde(rename_all = "camelCase")]
101pub struct PatchContentHashDto {
102    #[cfg_attr(feature = "openapi", schema(example = "example"))]
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub hash: Option<String>,
105    #[cfg_attr(feature = "openapi", schema(example = 42))]
106    #[serde(skip_serializing_if = "Option::is_none", alias = "size_bytes")]
107    pub size_bytes: Option<i64>,
108    #[cfg_attr(feature = "openapi", schema(example = "example"))]
109    #[serde(skip_serializing_if = "Option::is_none", alias = "storage_key")]
110    pub storage_key: Option<String>,
111    #[serde(skip_serializing_if = "Option::is_none", alias = "storage_backend")]
112    pub storage_backend: Option<StorageBackend>,
113    #[cfg_attr(feature = "openapi", schema(example = 42))]
114    #[serde(skip_serializing_if = "Option::is_none", alias = "reference_count")]
115    pub reference_count: Option<i32>,
116    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
117    #[serde(skip_serializing_if = "Option::is_none", alias = "first_uploaded_at")]
118    pub first_uploaded_at: Option<DateTime<Utc>>,
119    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
120    #[serde(skip_serializing_if = "Option::is_none", alias = "last_referenced_at")]
121    pub last_referenced_at: Option<DateTime<Utc>>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub fingerprint: Option<String>,
124}
125
126impl PatchContentHashDto {
127    /// Check if any field is set
128    pub fn has_changes(&self) -> bool {
129        self.hash.is_some() || self.size_bytes.is_some() || self.storage_key.is_some() || self.storage_backend.is_some() || self.reference_count.is_some() || self.first_uploaded_at.is_some() || self.last_referenced_at.is_some() || self.fingerprint.is_some()
130    }
131}
132
133// =============================================================================
134// Response DTO
135// =============================================================================
136
137/// Response DTO for ContentHash entity
138///
139/// Used in API responses for single entity.
140/// Includes all fields including metadata.
141#[derive(Debug, Clone, Serialize)]
142#[cfg_attr(feature = "openapi", derive(ToSchema))]
143#[serde(rename_all = "camelCase")]
144pub struct ContentHashResponseDto {
145    #[cfg_attr(feature = "openapi", schema(example = "550e8400-e29b-41d4-a716-446655440000"))]
146    pub id: Uuid,
147    #[cfg_attr(feature = "openapi", schema(example = "example"))]
148    pub hash: String,
149    #[cfg_attr(feature = "openapi", schema(example = 42))]
150    pub size_bytes: i64,
151    #[cfg_attr(feature = "openapi", schema(example = "example"))]
152    pub storage_key: String,
153    pub storage_backend: StorageBackend,
154    #[cfg_attr(feature = "openapi", schema(example = 42))]
155    pub reference_count: i32,
156    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
157    pub first_uploaded_at: DateTime<Utc>,
158    #[cfg_attr(feature = "openapi", schema(example = "2024-01-01T00:00:00Z"))]
159    pub last_referenced_at: DateTime<Utc>,
160    pub fingerprint: Option<String>,
161    pub metadata: AuditMetadata,
162}
163
164// =============================================================================
165// List Response DTO
166// =============================================================================
167
168/// Paginated list response for ContentHash entities
169///
170/// Used in API responses for list endpoints.
171/// Includes pagination metadata.
172#[derive(Debug, Clone, Serialize)]
173#[cfg_attr(feature = "openapi", derive(ToSchema))]
174#[serde(rename_all = "camelCase")]
175pub struct ContentHashListResponseDto {
176    /// List of items
177    pub items: Vec<ContentHashResponseDto>,
178    /// Total number of items (across all pages)
179    pub total: u64,
180    /// Current page number (1-indexed)
181    pub page: u32,
182    /// Number of items per page
183    pub per_page: u32,
184    /// Total number of pages
185    pub total_pages: u32,
186    /// Whether there is a next page
187    pub has_next: bool,
188    /// Whether there is a previous page
189    pub has_prev: bool,
190}
191
192impl ContentHashListResponseDto {
193    /// Create a new list response from items and pagination info
194    pub fn new(items: Vec<ContentHashResponseDto>, total: u64, page: u32, per_page: u32) -> Self {
195        let total_pages = if per_page > 0 {
196            ((total as f64) / (per_page as f64)).ceil() as u32
197        } else {
198            0
199        };
200        Self {
201            items,
202            total,
203            page,
204            per_page,
205            total_pages,
206            has_next: page < total_pages,
207            has_prev: page > 1,
208        }
209    }
210}
211
212/// Summary DTO for list views (compact version)
213#[derive(Debug, Clone, Serialize)]
214#[cfg_attr(feature = "openapi", derive(ToSchema))]
215#[serde(rename_all = "camelCase")]
216pub struct ContentHashSummaryDto {
217    pub id: Uuid,
218    pub hash: String,
219    pub size_bytes: i64,
220    pub storage_key: String,
221    pub created_at: Option<DateTime<Utc>>,
222}
223
224// =============================================================================
225// Conversions
226// =============================================================================
227
228impl From<ContentHash> for ContentHashResponseDto {
229    fn from(entity: ContentHash) -> Self {
230        Self {
231            id: entity.id,
232            hash: entity.hash,
233            size_bytes: entity.size_bytes,
234            storage_key: entity.storage_key,
235            storage_backend: entity.storage_backend,
236            reference_count: entity.reference_count,
237            first_uploaded_at: entity.first_uploaded_at,
238            last_referenced_at: entity.last_referenced_at,
239            fingerprint: entity.fingerprint,
240            metadata: entity.metadata,
241        }
242    }
243}
244
245impl From<ContentHash> for ContentHashSummaryDto {
246    fn from(entity: ContentHash) -> Self {
247        let created_at = backbone_core::PersistentEntity::created_at(&entity);
248        Self {
249            id: entity.id,
250            hash: entity.hash,
251            size_bytes: entity.size_bytes,
252            storage_key: entity.storage_key,
253            created_at,
254        }
255    }
256}
257
258impl From<CreateContentHashDto> for ContentHash {
259    fn from(dto: CreateContentHashDto) -> Self {
260        Self {
261            id: Uuid::new_v4(),
262            hash: dto.hash,
263            size_bytes: dto.size_bytes,
264            storage_key: dto.storage_key,
265            storage_backend: dto.storage_backend,
266            reference_count: dto.reference_count,
267            first_uploaded_at: dto.first_uploaded_at,
268            last_referenced_at: dto.last_referenced_at,
269            fingerprint: dto.fingerprint,
270            metadata: AuditMetadata::default(),
271        }
272    }
273}
274
275impl From<&ContentHash> for ContentHashResponseDto {
276    fn from(entity: &ContentHash) -> Self {
277        Self {
278            id: entity.id.clone(),
279            hash: entity.hash.clone(),
280            size_bytes: entity.size_bytes.clone(),
281            storage_key: entity.storage_key.clone(),
282            storage_backend: entity.storage_backend.clone(),
283            reference_count: entity.reference_count.clone(),
284            first_uploaded_at: entity.first_uploaded_at.clone(),
285            last_referenced_at: entity.last_referenced_at.clone(),
286            fingerprint: entity.fingerprint.clone(),
287            metadata: entity.metadata.clone(),
288        }
289    }
290}
291
292impl backbone_core::FromCreateDto<CreateContentHashDto> for ContentHash {
293    fn from_create_dto(dto: CreateContentHashDto) -> backbone_core::ServiceResult<Self> {
294        Ok(ContentHash::from(dto))
295    }
296}
297
298impl backbone_core::ApplyUpdateDto<UpdateContentHashDto> for ContentHash {
299    fn apply_update(mut self, dto: UpdateContentHashDto) -> backbone_core::ServiceResult<Self> {
300        self.hash = dto.hash;
301        self.size_bytes = dto.size_bytes;
302        self.storage_key = dto.storage_key;
303        self.storage_backend = dto.storage_backend;
304        self.reference_count = dto.reference_count;
305        self.first_uploaded_at = dto.first_uploaded_at;
306        self.last_referenced_at = dto.last_referenced_at;
307        self.fingerprint = dto.fingerprint;
308        Ok(self)
309    }
310}
311
312// =============================================================================
313// Custom DTOs
314// =============================================================================
315
316// Add custom DTOs specific to ContentHash here.
317// This section will be preserved during regeneration.
318// >>> END CUSTOM DTOs
319