#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum PhaseSelection {
#[default]
Default,
All,
Named(String),
}
impl From<&str> for PhaseSelection {
fn from(value: &str) -> Self {
match value {
"#ALL" => PhaseSelection::All,
"#DEFAULT" => PhaseSelection::Default,
other => PhaseSelection::Named(other.to_string()),
}
}
}
impl From<String> for PhaseSelection {
fn from(value: String) -> Self {
PhaseSelection::from(value.as_str())
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Default)]
pub struct ValidateOptions {
pub phase: PhaseSelection,
pub max_failures: Option<usize>,
pub record_fired_rules: bool,
pub parallel_patterns: bool,
pub current_time: Option<f64>,
}
impl ValidateOptions {
#[must_use]
pub fn new() -> Self {
Self {
phase: PhaseSelection::Default,
max_failures: None,
record_fired_rules: true,
parallel_patterns: false,
current_time: None,
}
}
#[must_use]
pub fn with_phase(mut self, phase: PhaseSelection) -> Self {
self.phase = phase;
self
}
#[must_use]
pub fn with_max_failures(mut self, max: usize) -> Self {
self.max_failures = Some(max);
self
}
#[must_use]
pub fn with_record_fired_rules(mut self, record: bool) -> Self {
self.record_fired_rules = record;
self
}
#[must_use]
pub fn with_parallel_patterns(mut self, parallel: bool) -> Self {
self.parallel_patterns = parallel;
self
}
#[must_use]
pub fn with_current_time(mut self, seconds: f64) -> Self {
self.current_time = Some(seconds);
self
}
pub(crate) fn resolve_current_time(&self) -> f64 {
self.current_time.unwrap_or_else(|| {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0.0, |elapsed| elapsed.as_secs_f64())
})
}
pub(crate) fn is_parallel(&self) -> bool {
self.parallel_patterns && self.max_failures.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn phase_keywords_are_recognised() {
assert_eq!(PhaseSelection::from("#ALL"), PhaseSelection::All);
assert_eq!(PhaseSelection::from("#DEFAULT"), PhaseSelection::Default);
assert_eq!(
PhaseSelection::from("x"),
PhaseSelection::Named("x".to_string())
);
}
#[test]
fn defaults_record_fired_rules() {
assert!(ValidateOptions::new().record_fired_rules);
assert_eq!(ValidateOptions::new().max_failures, None);
}
#[test]
fn parallelism_is_off_by_default() {
assert!(!ValidateOptions::new().parallel_patterns);
assert!(!ValidateOptions::new().is_parallel());
}
#[test]
fn max_failures_forces_sequential_evaluation() {
let options = ValidateOptions::new().with_parallel_patterns(true);
assert!(options.is_parallel());
assert!(!options.with_max_failures(5).is_parallel());
}
#[test]
fn builders_chain() {
let options = ValidateOptions::new()
.with_phase(PhaseSelection::All)
.with_max_failures(3)
.with_record_fired_rules(false)
.with_parallel_patterns(true);
assert_eq!(options.phase, PhaseSelection::All);
assert_eq!(options.max_failures, Some(3));
assert!(!options.record_fired_rules);
assert!(options.parallel_patterns);
}
}