use mimir_core::{ClockTime, Pipeline};
use crate::LibrarianError;
#[derive(Debug, Clone)]
pub struct PreEmitValidator {
scratch: Pipeline,
}
impl PreEmitValidator {
#[must_use]
pub fn new() -> Self {
Self {
scratch: Pipeline::new(),
}
}
pub fn validate(&mut self, candidate_lisp: &str) -> Result<(), LibrarianError> {
let now = ClockTime::now().map_err(|err| LibrarianError::ValidationClock {
message: err.to_string(),
})?;
self.validate_at(candidate_lisp, now)
}
pub fn validate_at(
&mut self,
candidate_lisp: &str,
now: ClockTime,
) -> Result<(), LibrarianError> {
let mut working = self.scratch.clone();
working
.compile_batch(candidate_lisp, now)
.map_err(|source| LibrarianError::ValidationRejected { source })?;
self.scratch = working;
Ok(())
}
}
impl Default for PreEmitValidator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructor_succeeds() {
let _validator = PreEmitValidator::new();
}
#[test]
fn default_is_equivalent_to_new() {
let _a = PreEmitValidator::new();
let _b = PreEmitValidator::default();
}
fn fixed_now() -> Result<ClockTime, mimir_core::ClockTimeError> {
ClockTime::try_from_millis(1_713_350_400_000)
}
#[test]
fn validate_accepts_valid_candidate() -> Result<(), Box<dyn std::error::Error>> {
let mut validator = PreEmitValidator::new();
validator.validate_at(
"(sem @alice @knows @bob :src @observation :c 0.8 :v 2024-01-15)",
fixed_now()?,
)?;
Ok(())
}
#[test]
fn validate_rejects_parse_error_without_poisoning_scratch(
) -> Result<(), Box<dyn std::error::Error>> {
let mut validator = PreEmitValidator::new();
assert!(matches!(
validator.validate_at("(sem @alice", fixed_now()?),
Err(LibrarianError::ValidationRejected {
source: mimir_core::PipelineError::Parse(_)
})
));
validator.validate_at(
"(sem @alice @knows @bob :src @observation :c 0.8 :v 2024-01-15)",
fixed_now()?,
)?;
Ok(())
}
#[test]
fn validate_commits_successes_to_scratch_state() -> Result<(), Box<dyn std::error::Error>> {
let mut validator = PreEmitValidator::new();
let candidate = "(sem @alice @knows @bob :src @observation :c 0.8 :v 2024-01-15)";
validator.validate_at(candidate, fixed_now()?)?;
assert!(matches!(
validator.validate_at(candidate, fixed_now()?),
Err(LibrarianError::ValidationRejected {
source: mimir_core::PipelineError::Emit(_)
})
));
Ok(())
}
}