made_core/value_objects/ceremony/
state_visit.rs1use serde::{Deserialize, Serialize};
2
3use crate::error::DomainError;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
7#[serde(try_from = "u32", into = "u32")]
8pub struct StateVisit(u32);
9
10impl StateVisit {
11 pub const FIRST: Self = Self(1);
12
13 pub fn new(value: u32) -> Result<Self, DomainError> {
14 if value == 0 {
15 return Err(DomainError::MustBeNonZero {
16 field: "state_visit",
17 });
18 }
19 Ok(Self(value))
20 }
21
22 pub fn next(self) -> Result<Self, DomainError> {
23 self.0
24 .checked_add(1)
25 .map(Self)
26 .ok_or(DomainError::InvariantViolated {
27 reason: "state visit coordinate exhausted",
28 })
29 }
30
31 #[must_use]
32 pub const fn get(self) -> u32 {
33 self.0
34 }
35
36 #[must_use]
37 pub const fn is_first(&self) -> bool {
38 self.0 == 1
39 }
40}
41
42impl Default for StateVisit {
43 fn default() -> Self {
44 Self::FIRST
45 }
46}
47impl TryFrom<u32> for StateVisit {
48 type Error = DomainError;
49 fn try_from(value: u32) -> Result<Self, Self::Error> {
50 Self::new(value)
51 }
52}
53impl From<StateVisit> for u32 {
54 fn from(value: StateVisit) -> Self {
55 value.0
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 #[test]
63 fn visits_refuse_zero_and_overflow() {
64 assert!(StateVisit::new(0).is_err());
65 assert!(serde_json::from_str::<StateVisit>("0").is_err());
66 assert!(StateVisit::new(u32::MAX).unwrap().next().is_err());
67 assert_eq!(StateVisit::FIRST.next().unwrap().get(), 2);
68 assert_eq!(serde_json::to_string(&StateVisit::FIRST).unwrap(), "1");
69 }
70}