1use rand::Rng;
6use std::env;
7
8use crate::constants::DST_SIMULATION_STEPS_MAX;
9
10#[derive(Debug, Clone, Copy)]
17pub struct SimConfig {
18 seed: u64,
20 steps_max: u64,
22}
23
24impl SimConfig {
25 #[must_use]
37 pub fn with_seed(seed: u64) -> Self {
38 let config = Self {
40 seed,
41 steps_max: DST_SIMULATION_STEPS_MAX,
42 };
43 assert_eq!(config.seed, seed, "seed must be stored correctly");
44 assert!(config.steps_max > 0, "steps_max must be positive");
45 config
46 }
47
48 #[must_use]
60 pub fn from_env_or_random() -> Self {
61 let seed = match env::var("DST_SEED") {
62 Ok(seed_str) => {
63 seed_str.parse::<u64>().unwrap_or_else(|_| {
65 panic!("DST_SEED must be a valid u64, got: {}", seed_str);
66 })
67 }
68 Err(_) => {
69 let seed = rand::thread_rng().gen::<u64>();
70 eprintln!("DST: Generated random seed (replay with DST_SEED={})", seed);
71 seed
72 }
73 };
74
75 Self::with_seed(seed)
76 }
77
78 #[must_use]
80 pub fn seed(&self) -> u64 {
81 self.seed
82 }
83
84 #[must_use]
86 pub fn steps_max(&self) -> u64 {
87 self.steps_max
88 }
89
90 #[must_use]
92 pub fn with_steps_max(self, steps_max: u64) -> Self {
93 assert!(steps_max > 0, "steps_max must be positive");
95
96 Self {
97 seed: self.seed,
98 steps_max,
99 }
100 }
101}
102
103impl Default for SimConfig {
104 fn default() -> Self {
105 Self::from_env_or_random()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_with_seed() {
115 let config = SimConfig::with_seed(12345);
116 assert_eq!(config.seed(), 12345);
117 assert_eq!(config.steps_max(), DST_SIMULATION_STEPS_MAX);
118 }
119
120 #[test]
121 fn test_with_seed_zero() {
122 let config = SimConfig::with_seed(0);
123 assert_eq!(config.seed(), 0);
124 }
125
126 #[test]
127 fn test_with_seed_max() {
128 let config = SimConfig::with_seed(u64::MAX);
129 assert_eq!(config.seed(), u64::MAX);
130 }
131
132 #[test]
133 fn test_with_steps_max() {
134 let config = SimConfig::with_seed(42).with_steps_max(100);
135 assert_eq!(config.seed(), 42);
136 assert_eq!(config.steps_max(), 100);
137 }
138
139 #[test]
140 #[should_panic(expected = "steps_max must be positive")]
141 fn test_with_steps_max_zero_panics() {
142 let _ = SimConfig::with_seed(42).with_steps_max(0);
143 }
144
145 #[test]
150 fn test_random_seed_generation() {
151 let _ = env::remove_var("DST_SEED");
153
154 let config = SimConfig::from_env_or_random();
157 assert!(config.seed() > 0 || config.seed() == 0); assert!(config.steps_max() > 0);
159 }
160}