Skip to main content

domain/daycare/
assignment.rs

1//! Daycare playgroup assignment decisions after eligibility and coverage are known.
2//!
3//! ```
4//! use domain::{daycare, entities};
5//! use uuid::Uuid;
6//!
7//! let request = daycare::assignment::Request::builder()
8//!     .pet_id(entities::PetId(Uuid::nil()))
9//!     .service(daycare::ServiceVariant::AllDayPlay)
10//!     .eligibility(daycare::eligibility::GroupPlayDecision::Eligible {
11//!         basis: daycare::eligibility::EligibleBasis::CurrentEvidence,
12//!     })
13//!     .coverage(daycare::coverage::Decision::Sufficient)
14//!     .playgroup(daycare::assignment::PlaygroupId::try_new("small-dogs-am").unwrap())
15//!     .build();
16//!
17//! assert!(matches!(
18//!     daycare::assignment::Service.assign(request),
19//!     daycare::assignment::Decision::Assigned { .. }
20//! ));
21//! ```
22
23use super::*;
24use crate::policy;
25
26pub use playgroup_id::Id as PlaygroupId;
27
28/// Playgroup identifier chosen from scheduling/source data for daycare assignment review.
29pub mod playgroup_id {
30    use super::*;
31
32    #[nutype(
33        sanitize(trim),
34        validate(not_empty, len_char_max = 120),
35        derive(
36            Debug,
37            Clone,
38            PartialEq,
39            Eq,
40            PartialOrd,
41            Ord,
42            Hash,
43            Serialize,
44            Deserialize
45        )
46    )]
47    pub struct Id(String);
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
51/// Assignment request that joins pet, service, eligibility, coverage, and target playgroup evidence.
52pub struct Request {
53    /// Pet being considered for playgroup assignment.
54    pub pet_id: PetId,
55    /// Requested service that drives scheduling and labor estimates.
56    pub service: ServiceVariant,
57    /// Group-play eligibility decision that must be clear before assignment.
58    pub eligibility: eligibility::GroupPlayDecision,
59    /// Staffing coverage decision that must be sufficient before assignment.
60    pub coverage: coverage::Decision,
61    /// Candidate playgroup selected from resort operations data.
62    pub playgroup: PlaygroupId,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66/// Playgroup assignment outcome staff can act on or review.
67pub enum Decision {
68    /// Pet may be placed in the requested playgroup.
69    Assigned {
70        /// Pet being considered for playgroup assignment.
71        pet_id: PetId,
72        /// Candidate playgroup selected from resort operations data.
73        playgroup: PlaygroupId,
74    },
75    /// Pet is eligible, but staffing coverage prevents immediate assignment.
76    Waitlist {
77        /// Operational reason the assignment is not automatically clear.
78        reason: WaitlistReason,
79        /// Human review gate required before staff override this assignment outcome.
80        gate: policy::ReviewGate,
81    },
82    /// Pet cannot be assigned until eligibility or safety review clears.
83    Blocked {
84        /// Operational reason the assignment is not automatically clear.
85        reason: BlockReason,
86        /// Human review gate required before staff override this assignment outcome.
87        gate: policy::ReviewGate,
88    },
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
92/// Reasons a daycare assignment should waitlist instead of placing the pet.
93pub enum WaitlistReason {
94    /// Staffing ratio or roster evidence does not support another playgroup assignment.
95    StaffCoverageInsufficient,
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99/// Reasons assignment is blocked rather than merely waitlisted.
100pub enum BlockReason {
101    /// Group-play eligibility is not clear enough for playgroup assignment.
102    EligibilityNotCleared,
103}
104
105#[derive(Debug, Clone, Default)]
106/// Deterministic daycare assignment service for playgroup placement decisions.
107pub struct Service;
108
109impl Service {
110    /// Assigns, waitlists, or blocks a pet from playgroup placement using eligibility and coverage evidence.
111    pub fn assign(&self, request: Request) -> Decision {
112        match (&request.eligibility, &request.coverage) {
113            (eligibility::GroupPlayDecision::Eligible { .. }, coverage::Decision::Sufficient) => {
114                Decision::Assigned {
115                    pet_id: request.pet_id,
116                    playgroup: request.playgroup,
117                }
118            }
119            (
120                eligibility::GroupPlayDecision::Eligible { .. },
121                coverage::Decision::Insufficient { gate, .. },
122            ) => Decision::Waitlist {
123                reason: WaitlistReason::StaffCoverageInsufficient,
124                gate: gate.clone(),
125            },
126            (
127                eligibility::GroupPlayDecision::Eligible { .. },
128                coverage::Decision::Unknown { gate },
129            ) => Decision::Waitlist {
130                reason: WaitlistReason::StaffCoverageInsufficient,
131                gate: gate.clone(),
132            },
133            _ => Decision::Blocked {
134                reason: BlockReason::EligibilityNotCleared,
135                gate: policy::ReviewGate::BehaviorReview,
136            },
137        }
138    }
139}