klieo-spec 3.3.0

Generic decompose / critique / refine traits and a quality loop runner.
Documentation
//! Generic critique trait.

use crate::error::SpecError;
use crate::quality::Critique;
use async_trait::async_trait;

/// Evaluate the quality of a candidate `T`.
///
/// The `iter` argument is the 0-based iteration index — useful for
/// implementations that loosen their bar over time, log iteration-
/// scoped telemetry, or short-circuit early iterations cheaply.
#[async_trait]
pub trait Critic<T>: Send + Sync
where
    T: Send + Sync,
{
    /// Evaluate `t` at iteration `iter`.
    async fn evaluate(&self, t: &T, iter: u8) -> Result<Critique, SpecError>;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[allow(dead_code)]
    fn _trait_is_object_safe(_c: &dyn Critic<String>) {}

    struct LenCritic {
        min: usize,
    }
    #[async_trait]
    impl Critic<String> for LenCritic {
        async fn evaluate(&self, t: &String, _i: u8) -> Result<Critique, SpecError> {
            if t.len() >= self.min {
                Ok(Critique::pass(format!("ok: {}", t.len())))
            } else {
                Ok(Critique::fail(format!("short: {}", t.len())))
            }
        }
    }

    #[tokio::test]
    async fn len_critic_passes_when_long_enough() {
        let c = LenCritic { min: 3 };
        let crit = c.evaluate(&"hi".to_string(), 0).await.unwrap();
        assert!(!crit.pass);

        let crit = c.evaluate(&"hello".to_string(), 0).await.unwrap();
        assert!(crit.pass);
    }
}