solverforge_config/
solver_config.rs1use std::num::NonZeroUsize;
2use std::path::Path;
3use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7use crate::director::DirectorConfig;
8use crate::error::ConfigError;
9use crate::phase::PhaseConfig;
10use crate::termination::TerminationConfig;
11
12#[derive(Debug, Clone, Default, Deserialize, Serialize)]
14#[serde(rename_all = "snake_case")]
15pub struct SolverConfig {
16 #[serde(default)]
18 pub environment_mode: EnvironmentMode,
19
20 #[serde(default)]
22 pub random_seed: Option<u64>,
23
24 #[serde(default)]
26 pub move_thread_count: MoveThreadCount,
27
28 #[serde(default)]
30 pub termination: Option<TerminationConfig>,
31
32 #[serde(default)]
34 pub score_director: Option<DirectorConfig>,
35
36 #[serde(default)]
38 pub phases: Vec<PhaseConfig>,
39
40 #[serde(default)]
46 pub candidate_trace: Option<CandidateTraceConfig>,
47}
48
49impl SolverConfig {
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn load(path: impl AsRef<Path>) -> Result<Self, ConfigError> {
60 Self::from_toml_file(path)
61 }
62
63 pub fn from_toml_file(path: impl AsRef<Path>) -> Result<Self, ConfigError> {
65 let contents = std::fs::read_to_string(path)?;
66 Self::from_toml_str(&contents)
67 }
68
69 pub fn from_toml_str(s: &str) -> Result<Self, ConfigError> {
71 Ok(toml::from_str(s)?)
72 }
73
74 pub fn from_yaml_file(path: impl AsRef<Path>) -> Result<Self, ConfigError> {
76 let contents = std::fs::read_to_string(path)?;
77 Self::from_yaml_str(&contents)
78 }
79
80 pub fn from_yaml_str(s: &str) -> Result<Self, ConfigError> {
82 Ok(serde_yaml::from_str(s)?)
83 }
84
85 pub fn with_termination_seconds(mut self, seconds: u64) -> Self {
86 self.termination = Some(TerminationConfig {
87 seconds_spent_limit: Some(seconds),
88 ..self.termination.unwrap_or_default()
89 });
90 self
91 }
92
93 pub fn with_random_seed(mut self, seed: u64) -> Self {
94 self.random_seed = Some(seed);
95 self
96 }
97
98 pub fn with_phase(mut self, phase: PhaseConfig) -> Self {
99 self.phases.push(phase);
100 self
101 }
102
103 pub fn canonical_toml(&self) -> String {
110 toml::to_string(self)
111 .expect("SolverConfig serialization must succeed for a serde-derived configuration")
112 }
113
114 pub fn canonical_phase_toml(phase: &PhaseConfig) -> String {
120 Self {
121 phases: vec![phase.clone()],
122 ..Self::default()
123 }
124 .canonical_toml()
125 }
126
127 pub fn time_limit(&self) -> Option<Duration> {
145 self.termination.as_ref().and_then(|t| t.time_limit())
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
162#[serde(rename_all = "snake_case")]
163pub struct CandidateTraceConfig {
164 pub max_entries: NonZeroUsize,
165}
166
167impl CandidateTraceConfig {
168 pub const fn new(max_entries: NonZeroUsize) -> Self {
169 Self { max_entries }
170 }
171}
172
173#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
175#[serde(rename_all = "snake_case")]
176pub enum EnvironmentMode {
177 #[default]
179 NonReproducible,
180
181 Reproducible,
183
184 FastAssert,
186
187 FullAssert,
189}
190
191#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
193#[serde(rename_all = "snake_case")]
194pub enum MoveThreadCount {
195 #[default]
197 Auto,
198
199 None,
201
202 Count(usize),
204}
205
206#[derive(Debug, Clone, Default)]
208pub struct SolverConfigOverride {
209 pub termination: Option<TerminationConfig>,
211}
212
213impl SolverConfigOverride {
214 pub fn with_termination(termination: TerminationConfig) -> Self {
215 SolverConfigOverride {
216 termination: Some(termination),
217 }
218 }
219}