made_core/value_objects/
num_agents.rs1use std::fmt;
4use std::num::NonZeroU32;
5
6use serde::{Deserialize, Serialize};
7
8use crate::error::DomainError;
9
10pub const MAX_NUM_AGENTS: u32 = 64;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18#[serde(transparent)]
19pub struct NumAgents(NonZeroU32);
20
21impl NumAgents {
22 pub fn new(value: u32) -> Result<Self, DomainError> {
23 if value > MAX_NUM_AGENTS {
24 return Err(DomainError::OutOfRange {
25 field: "num_agents",
26 value: f64::from(value),
27 min: 1.0,
28 max: f64::from(MAX_NUM_AGENTS),
29 });
30 }
31 let non_zero = NonZeroU32::new(value).ok_or(DomainError::MustBeNonZero {
32 field: "num_agents",
33 })?;
34 Ok(Self(non_zero))
35 }
36
37 #[must_use]
38 pub fn get(self) -> u32 {
39 self.0.get()
40 }
41}
42
43impl fmt::Display for NumAgents {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}", self.0.get())
46 }
47}
48
49impl TryFrom<u32> for NumAgents {
50 type Error = DomainError;
51 fn try_from(value: u32) -> Result<Self, Self::Error> {
52 Self::new(value)
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn one_is_minimum() {
62 assert_eq!(NumAgents::new(1).unwrap().get(), 1);
63 }
64
65 #[test]
66 fn zero_is_rejected() {
67 assert!(matches!(
68 NumAgents::new(0).unwrap_err(),
69 DomainError::MustBeNonZero {
70 field: "num_agents"
71 }
72 ));
73 }
74
75 #[test]
76 fn upper_bound_is_accepted() {
77 assert!(NumAgents::new(MAX_NUM_AGENTS).is_ok());
78 }
79
80 #[test]
81 fn above_upper_bound_is_rejected() {
82 assert!(matches!(
83 NumAgents::new(MAX_NUM_AGENTS + 1).unwrap_err(),
84 DomainError::OutOfRange {
85 field: "num_agents",
86 ..
87 }
88 ));
89 }
90
91 #[test]
92 fn serde_is_transparent() {
93 assert_eq!(
94 serde_json::to_string(&NumAgents::new(3).unwrap()).unwrap(),
95 "3"
96 );
97 }
98}