Skip to main content

domain/
document.rs

1//! Document intake, safety review, storage, and retention values.
2//!
3//! Documents carry vaccine proofs, waivers, medical records, incident evidence, and other source
4//! artifacts that staff and agents rely on. The domain separates received/extracted facts from
5//! verified facts, records virus/PII handling state, and keeps storage references explicit so
6//! automation cannot treat unreviewed uploads as compliance truth.
7
8use bon::Builder;
9use nutype::nutype;
10#[allow(unused_imports)]
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15/// Document classification used to route vaccine, waiver, medical, photo, and incident evidence.
16pub enum Classification {
17    /// Vaccine proof document classification or pipeline state used for review and retention.
18    VaccineProof,
19    /// Waiver document classification or pipeline state used for review and retention.
20    Waiver,
21    /// Photo document classification or pipeline state used for review and retention.
22    Photo,
23    /// Medical record document classification or pipeline state used for review and retention.
24    MedicalRecord,
25    /// Incident evidence document classification or pipeline state used for review and retention.
26    IncidentEvidence,
27    /// Non-dog, non-cat pet handled by exception policy.
28    Other,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32/// Source route through which a document entered the review and storage pipeline.
33pub enum Source {
34    /// Customer upload document classification or pipeline state used for review and retention.
35    CustomerUpload,
36    /// Staff scan document classification or pipeline state used for review and retention.
37    StaffScan,
38    /// Staff upload document classification or pipeline state used for review and retention.
39    StaffUpload,
40    /// Email ingest document classification or pipeline state used for review and retention.
41    EmailIngest,
42    /// Provider poll document classification or pipeline state used for review and retention.
43    ProviderPoll,
44    /// Provider webhook document classification or pipeline state used for review and retention.
45    ProviderWebhook,
46    /// Migration import document classification or pipeline state used for review and retention.
47    MigrationImport,
48    /// Provider role or status could not be mapped confidently.
49    Unknown,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
53/// Normalized lifecycle states used to reconcile source-system data with domain workflows.
54pub enum Status {
55    /// Received document classification or pipeline state used for review and retention.
56    Received,
57    /// Quarantined rejected document classification or pipeline state used for review and retention.
58    QuarantinedRejected,
59    /// Extracting document classification or pipeline state used for review and retention.
60    Extracting,
61    /// Extraction failed document classification or pipeline state used for review and retention.
62    ExtractionFailed,
63    /// Awaiting review document classification or pipeline state used for review and retention.
64    AwaitingReview,
65    /// Verified document classification or pipeline state used for review and retention.
66    Verified,
67    /// Rejected document classification or pipeline state used for review and retention.
68    Rejected,
69    /// Superseded document classification or pipeline state used for review and retention.
70    Superseded,
71    /// Archived document classification or pipeline state used for review and retention.
72    Archived,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
76/// Virus-scan outcome used before documents may become staff-visible evidence.
77pub enum VirusScanStatus {
78    /// Pending document classification or pipeline state used for review and retention.
79    Pending,
80    /// Passed document classification or pipeline state used for review and retention.
81    Passed,
82    /// Deposit collection was attempted but did not succeed.
83    Failed,
84    /// Unsupported document classification or pipeline state used for review and retention.
85    Unsupported,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
89/// PII redaction state used before document content is exposed to agents or reports.
90pub enum PiiRedactionStatus {
91    /// No deposit or review is needed for this reservation path.
92    NotRequired,
93    /// Pending document classification or pipeline state used for review and retention.
94    Pending,
95    /// Redacted document classification or pipeline state used for review and retention.
96    Redacted,
97    /// Deposit collection was attempted but did not succeed.
98    Failed,
99}
100
101#[nutype(
102    sanitize(trim),
103    validate(not_empty, len_char_max = 255),
104    derive(
105        Debug,
106        Clone,
107        PartialEq,
108        Eq,
109        PartialOrd,
110        Ord,
111        Hash,
112        Serialize,
113        Deserialize
114    )
115)]
116pub struct FileName(String);
117
118/// MIME type reported for a document before extraction, virus scanning, or storage policy decisions.
119#[nutype(
120    sanitize(trim),
121    validate(not_empty, len_char_max = 160),
122    derive(
123        Debug,
124        Clone,
125        PartialEq,
126        Eq,
127        PartialOrd,
128        Ord,
129        Hash,
130        Serialize,
131        Deserialize
132    )
133)]
134pub struct MimeType(String);
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
137/// Non-zero document size used to reject empty uploads before extraction or review.
138pub struct ContentLengthBytes(u64);
139
140impl ContentLengthBytes {
141    /// Promotes boundary input into a validated document domain value.
142    pub const fn try_new(value: u64) -> Result<Self, ContentLengthError> {
143        if value == 0 {
144            return Err(ContentLengthError::EmptyObject);
145        }
146        Ok(Self(value))
147    }
148
149    /// Exposes the validated scalar for serialization and adapter boundaries.
150    pub const fn get(self) -> u64 {
151        self.0
152    }
153}
154
155impl<'de> Deserialize<'de> for ContentLengthBytes {
156    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
157    where
158        D: serde::Deserializer<'de>,
159    {
160        Self::try_new(u64::deserialize(deserializer)?).map_err(serde::de::Error::custom)
161    }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
165/// Validation error for document size constraints.
166pub enum ContentLengthError {
167    #[error("document storage evidence must not point at an empty object")]
168    /// Signals that object was blank or missing during document validation.
169    EmptyObject,
170}
171
172#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
173/// SHA-256 digest used to detect duplicate, tampered, or drifted document payloads.
174pub struct Sha256Digest(String);
175
176impl Sha256Digest {
177    /// Validates and creates the document value.
178    pub fn try_new(value: impl Into<String>) -> Result<Self, Sha256DigestError> {
179        let value = value.into().trim().to_ascii_lowercase();
180        if value.len() != 64 || !value.chars().all(|ch| ch.is_ascii_hexdigit()) {
181            return Err(Sha256DigestError::InvalidSha256Hex);
182        }
183        Ok(Self(value))
184    }
185
186    /// Returns the owned inner string for storage or outbound mapping.
187    pub fn into_inner(self) -> String {
188        self.0
189    }
190}
191
192impl fmt::Debug for Sha256Digest {
193    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
194        formatter.write_str("Sha256Digest(<redacted>)")
195    }
196}
197
198impl<'de> Deserialize<'de> for Sha256Digest {
199    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
200    where
201        D: serde::Deserializer<'de>,
202    {
203        Self::try_new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
204    }
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
208/// Validation error for document hash formatting.
209pub enum Sha256DigestError {
210    #[error("document content hashes must be 64 lowercase/uppercase hexadecimal sha256 characters")]
211    /// Signals that sha256 hex could not be parsed or accepted during document validation.
212    InvalidSha256Hex,
213}
214
215#[nutype(
216    sanitize(trim),
217    validate(not_empty, len_char_max = 160),
218    derive(
219        Debug,
220        Clone,
221        PartialEq,
222        Eq,
223        PartialOrd,
224        Ord,
225        Hash,
226        Serialize,
227        Deserialize
228    )
229)]
230pub struct StorageBucket(String);
231
232/// Storage key for the immutable document object used as review or compliance evidence.
233#[nutype(
234    sanitize(trim),
235    validate(not_empty, len_char_max = 500),
236    derive(
237        Debug,
238        Clone,
239        PartialEq,
240        Eq,
241        PartialOrd,
242        Ord,
243        Hash,
244        Serialize,
245        Deserialize
246    )
247)]
248pub struct StorageKey(String);
249
250/// Optional object-version marker for document retention and supersession audits.
251#[nutype(
252    sanitize(trim),
253    validate(not_empty, len_char_max = 160),
254    derive(
255        Debug,
256        Clone,
257        PartialEq,
258        Eq,
259        PartialOrd,
260        Ord,
261        Hash,
262        Serialize,
263        Deserialize
264    )
265)]
266pub struct StorageVersion(String);
267
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
269/// Storage pointer to the immutable object that backs a reviewed or source document.
270pub struct StorageRef {
271    /// Bucket fact promoted into this document contract.
272    pub bucket: StorageBucket,
273    /// Key fact promoted into this document contract.
274    pub key: StorageKey,
275    /// Version fact promoted into this document contract.
276    pub version: StorageVersion,
277}
278
279#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
280/// Original uploaded file metadata preserved for audit, extraction, and staff review.
281pub struct OriginalFile {
282    /// Filename fact promoted into this document contract.
283    pub filename: FileName,
284    /// Mime type fact promoted into this document contract.
285    pub mime_type: MimeType,
286    /// Content length fact promoted into this document contract.
287    pub content_length: ContentLengthBytes,
288    /// Sha 256 fact promoted into this document contract.
289    pub sha256: Sha256Digest,
290}