Skip to main content

domain/reservation/
mod.rs

1mod error;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5pub use error::{Error, Result};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
8/// Minimum pet age, in weeks, required before a service can be booked.
9pub struct MinimumAgeWeeks(u8);
10
11impl MinimumAgeWeeks {
12    /// Promotes reservation policy input after enforcing the domain validation rule.
13    pub fn try_new(value: u8) -> Result<Self> {
14        if value == 0 {
15            return Err(Error::EmptyMinimumAge);
16        }
17        Ok(Self(value))
18    }
19
20    /// Exposes the validated scalar for serialization and adapter boundaries.
21    pub const fn get(self) -> u8 {
22        self.0
23    }
24}
25
26impl<'de> Deserialize<'de> for MinimumAgeWeeks {
27    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
28    where
29        D: Deserializer<'de>,
30    {
31        Self::try_new(u8::deserialize(deserializer)?).map_err(serde::de::Error::custom)
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36/// Reasons a pet may be blocked by age rules for a service.
37pub enum AgePolicyReason {
38    /// Minimum-age rule for boarding reservations.
39    BoardingMinimum,
40    /// Minimum-age rule for day-play reservations.
41    DayPlayMinimum,
42    /// Minimum-age rule for daycare reservations.
43    DaycareMinimum,
44    /// Minimum-age rule configured for a specific service.
45    ServiceSpecificMinimum,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49/// Age gate applied when determining whether a pet may book a service.
50pub struct AgeThreshold {
51    minimum: MinimumAgeWeeks,
52    reason: AgePolicyReason,
53}
54
55impl AgeThreshold {
56    /// Assembles a reservation policy value from validated age and reason parts.
57    pub const fn new(minimum: MinimumAgeWeeks, reason: AgePolicyReason) -> Self {
58        Self { minimum, reason }
59    }
60
61    /// Returns the reservation minimum used by the policy gate.
62    pub const fn minimum(&self) -> MinimumAgeWeeks {
63        self.minimum
64    }
65
66    /// Returns the reservation reason used by the policy gate.
67    pub const fn reason(&self) -> AgePolicyReason {
68        self.reason
69    }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
73/// Display label for an optional reservation add-on offered to the customer.
74pub struct AddOnLabel(String);
75
76impl AddOnLabel {
77    /// Promotes reservation policy input after enforcing the domain validation rule.
78    pub fn try_new(value: impl Into<String>) -> Result<Self> {
79        let value = value.into().trim().to_string();
80        if value.is_empty() {
81            return Err(Error::EmptyAddOnLabel);
82        }
83        if value.chars().count() > 120 {
84            return Err(Error::AddOnLabelTooLong);
85        }
86        Ok(Self(value))
87    }
88
89    /// Returns the owned inner string for storage or outbound mapping.
90    pub fn into_inner(self) -> String {
91        self.0
92    }
93}
94
95impl<'de> Deserialize<'de> for AddOnLabel {
96    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
97    where
98        D: Deserializer<'de>,
99    {
100        Self::try_new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
101    }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105/// Business reasons for moving or rejecting a reservation workflow transition.
106pub enum TransitionReason {
107    /// Transition was initiated by a customer request.
108    CustomerRequested,
109    /// Transition was blocked because the requested capacity is unavailable.
110    CapacityUnavailable,
111    /// Transition is blocked by a non-overridable policy.
112    PolicyHardStop,
113    /// Transition is blocked until required customer or pet details are supplied.
114    MissingRequiredInformation,
115    /// Staff manually approved a workflow transition.
116    StaffOverride,
117}