Skip to main content

made_core/value_objects/delivery/
attention_reason.rs

1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::error::DomainError;
6
7const MAX_LEN: usize = 1000;
8
9/// Why a ceremony is asking for the integrator's attention.
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
11#[serde(transparent)]
12pub struct AttentionReason(String);
13
14impl AttentionReason {
15    /// Construct a validated value.
16    pub fn new(raw: impl Into<String>) -> Result<Self, DomainError> {
17        let raw = raw.into();
18        let value = raw.trim();
19        if value.is_empty() {
20            return Err(DomainError::EmptyField {
21                field: "attention_reason",
22            });
23        }
24        if value.len() > MAX_LEN {
25            return Err(DomainError::FieldTooLong {
26                field: "attention_reason",
27                actual: value.len(),
28                max: MAX_LEN,
29            });
30        }
31        if value
32            .chars()
33            .any(|character| character.is_control() && character != '\n' && character != '\t')
34        {
35            return Err(DomainError::InvalidCharacters {
36                field: "attention_reason",
37            });
38        }
39        Ok(Self(value.to_owned()))
40    }
41
42    #[must_use]
43    pub fn as_str(&self) -> &str {
44        &self.0
45    }
46}
47
48impl fmt::Display for AttentionReason {
49    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        formatter.write_str(&self.0)
51    }
52}
53
54// Deserialisation goes through the constructor: a stored or host-supplied
55// value must satisfy the same invariants as one built in process.
56impl<'de> Deserialize<'de> for AttentionReason {
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: Deserializer<'de>,
60    {
61        Self::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn empty_and_oversized_values_are_refused() {
71        assert!(AttentionReason::new("  ").is_err());
72        assert!(AttentionReason::new("x".repeat(MAX_LEN + 1)).is_err());
73        assert_eq!(AttentionReason::new("  value  ").unwrap().as_str(), "value");
74    }
75
76    #[test]
77    fn serde_reuses_constructor_validation() {
78        assert!(serde_json::from_str::<AttentionReason>("\"\"").is_err());
79        assert_eq!(
80            serde_json::from_str::<AttentionReason>("\"value\"")
81                .unwrap()
82                .as_str(),
83            "value"
84        );
85    }
86}