domain/daycare/incident.rs
1//! Daycare incident disposition rules that preserve pet-safety review gates.
2//!
3//! ```
4//! use domain::{daycare, entities, policy};
5//! use uuid::Uuid;
6//!
7//! let pet_id = entities::PetId(Uuid::nil());
8//! let disposition = daycare::incident::Classifier
9//! .classify(pet_id, daycare::incident::Severity::SuspendGroupPlay);
10//!
11//! assert_eq!(disposition.required_gate(), Some(policy::ReviewGate::ManagerApproval));
12//! assert_eq!(
13//! disposition.restriction(),
14//! daycare::incident::Restriction::SuspendedPendingManagerReview { pet_id },
15//! );
16//! ```
17
18use super::*;
19use crate::{entities, policy};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22/// Daycare incident handling policy that determines notes, customer notice, and group-play suspension.
23pub enum Policy {
24 /// Incident is recorded as a staff note without extra customer or manager workflow.
25 StaffNoteOnly,
26 /// Incident requires manager review and customer-message approval before follow-up.
27 ManagerReviewAndCustomerNotice,
28 /// Incident should suspend group play until manager review clears the pet.
29 SuspendGroupPlayPendingReview,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33/// Severity classification supplied to the incident classifier.
34pub enum Severity {
35 /// Incident is recorded as a staff note without extra customer or manager workflow.
36 StaffNoteOnly,
37 /// Incident requires customer notice but does not itself suspend group play.
38 OwnerNotice,
39 /// Incident requires manager review before staff close the disposition.
40 ManagerReview,
41 /// Incident should suspend group play pending manager review.
42 SuspendGroupPlay,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46/// Operational restriction created by a daycare incident disposition.
47pub enum Restriction {
48 /// Incident creates no active restriction on future daycare attendance.
49 None,
50 /// Pet whose group-play access is suspended pending manager review.
51 SuspendedPendingManagerReview {
52 /// Pet id carried by this variant.
53 pet_id: PetId,
54 },
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58/// Classified incident outcome carrying restriction and review-gate evidence.
59pub struct Disposition {
60 /// Pet whose group-play access is suspended pending manager review.
61 pub pet_id: PetId,
62 /// Severity used to choose restriction and review gates.
63 pub severity: Severity,
64 restriction: Restriction,
65 required_gate: Option<policy::ReviewGate>,
66}
67
68impl Disposition {
69 /// Returns the operational restriction that eligibility policy must honor.
70 pub const fn restriction(&self) -> Restriction {
71 self.restriction
72 }
73
74 /// Returns the human review gate required before the incident disposition can be cleared.
75 pub fn required_gate(&self) -> Option<policy::ReviewGate> {
76 self.required_gate.clone()
77 }
78}
79
80#[derive(Debug, Clone, Default)]
81/// Deterministic classifier that maps incident severity to restrictions and review gates.
82pub struct Classifier;
83
84impl Classifier {
85 /// Classifies an incident severity for a pet into a disposition staff can act on.
86 pub fn classify(&self, pet_id: entities::PetId, severity: Severity) -> Disposition {
87 let (restriction, required_gate) = match severity {
88 Severity::StaffNoteOnly => (Restriction::None, None),
89 Severity::OwnerNotice => (
90 Restriction::None,
91 Some(policy::ReviewGate::CustomerMessageApproval),
92 ),
93 Severity::ManagerReview => {
94 (Restriction::None, Some(policy::ReviewGate::ManagerApproval))
95 }
96 Severity::SuspendGroupPlay => (
97 Restriction::SuspendedPendingManagerReview { pet_id },
98 Some(policy::ReviewGate::ManagerApproval),
99 ),
100 };
101 Disposition {
102 pet_id,
103 severity,
104 restriction,
105 required_gate,
106 }
107 }
108}