made_core/value_objects/delivery/
host_kind.rs1use std::fmt;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::error::DomainError;
6
7const MAX_LEN: usize = 64;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
11#[serde(transparent)]
12pub struct HostKind(String);
13
14impl HostKind {
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 { field: "host_kind" });
21 }
22 if value.len() > MAX_LEN {
23 return Err(DomainError::FieldTooLong {
24 field: "host_kind",
25 actual: value.len(),
26 max: MAX_LEN,
27 });
28 }
29 if value
30 .chars()
31 .any(|character| character.is_control() && character != '\n' && character != '\t')
32 {
33 return Err(DomainError::InvalidCharacters { field: "host_kind" });
34 }
35 Ok(Self(value.to_owned()))
36 }
37
38 #[must_use]
39 pub fn as_str(&self) -> &str {
40 &self.0
41 }
42}
43
44impl fmt::Display for HostKind {
45 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46 formatter.write_str(&self.0)
47 }
48}
49
50impl<'de> Deserialize<'de> for HostKind {
53 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
54 where
55 D: Deserializer<'de>,
56 {
57 Self::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn empty_and_oversized_values_are_refused() {
67 assert!(HostKind::new(" ").is_err());
68 assert!(HostKind::new("x".repeat(MAX_LEN + 1)).is_err());
69 assert_eq!(HostKind::new(" value ").unwrap().as_str(), "value");
70 }
71
72 #[test]
73 fn serde_reuses_constructor_validation() {
74 assert!(serde_json::from_str::<HostKind>("\"\"").is_err());
75 assert_eq!(
76 serde_json::from_str::<HostKind>("\"value\"")
77 .unwrap()
78 .as_str(),
79 "value"
80 );
81 }
82}