made_core/value_objects/delivery/
host_address.rs1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::error::DomainError;
6
7const MAX_LEN: usize = 512;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
11#[serde(transparent)]
12pub struct HostAddress(String);
13
14impl HostAddress {
15 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: "host_address",
22 });
23 }
24 if value.len() > MAX_LEN {
25 return Err(DomainError::FieldTooLong {
26 field: "host_address",
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: "host_address",
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 HostAddress {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 formatter.write_str(&self.0)
51 }
52}
53
54impl<'de> Deserialize<'de> for HostAddress {
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!(HostAddress::new(" ").is_err());
72 assert!(HostAddress::new("x".repeat(MAX_LEN + 1)).is_err());
73 assert_eq!(HostAddress::new(" value ").unwrap().as_str(), "value");
74 }
75
76 #[test]
77 fn serde_reuses_constructor_validation() {
78 assert!(serde_json::from_str::<HostAddress>("\"\"").is_err());
79 assert_eq!(
80 serde_json::from_str::<HostAddress>("\"value\"")
81 .unwrap()
82 .as_str(),
83 "value"
84 );
85 }
86}