use super::error::{CapabilityError, ErrorCategory};
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use crate::gates::validation::{ValidationPolicy, ValidationReport};
use crate::types::{Draft, Proposal};
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone)]
pub enum ValidatorError {
CheckFailed {
check_name: String,
reason: String,
},
PolicyViolation {
policy: String,
message: String,
},
MissingEvidence {
expected: String,
},
Unavailable {
message: String,
},
Timeout {
elapsed: Duration,
deadline: Duration,
},
Internal {
message: String,
},
}
impl std::fmt::Display for ValidatorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CheckFailed { check_name, reason } => {
write!(f, "validation check '{}' failed: {}", check_name, reason)
}
Self::PolicyViolation { policy, message } => {
write!(f, "policy '{}' violated: {}", policy, message)
}
Self::MissingEvidence { expected } => {
write!(f, "missing required evidence: {}", expected)
}
Self::Unavailable { message } => write!(f, "validator unavailable: {}", message),
Self::Timeout { elapsed, deadline } => {
write!(
f,
"validation timeout after {:?} (deadline: {:?})",
elapsed, deadline
)
}
Self::Internal { message } => write!(f, "internal validator error: {}", message),
}
}
}
impl std::error::Error for ValidatorError {}
impl CapabilityError for ValidatorError {
fn category(&self) -> ErrorCategory {
match self {
Self::CheckFailed { .. } => ErrorCategory::InvalidInput,
Self::PolicyViolation { .. } => ErrorCategory::InvalidInput,
Self::MissingEvidence { .. } => ErrorCategory::InvalidInput,
Self::Unavailable { .. } => ErrorCategory::Unavailable,
Self::Timeout { .. } => ErrorCategory::Timeout,
Self::Internal { .. } => ErrorCategory::Internal,
}
}
fn is_transient(&self) -> bool {
matches!(self, Self::Unavailable { .. } | Self::Timeout { .. })
}
fn is_retryable(&self) -> bool {
self.is_transient() || matches!(self, Self::Internal { .. })
}
fn retry_after(&self) -> Option<Duration> {
None
}
}
pub trait Validator: Send + Sync {
type ValidateFut<'a>: Future<Output = Result<ValidationReport, ValidatorError>> + Send + 'a
where
Self: 'a;
fn validate<'a>(
&'a self,
proposal: &'a Proposal<Draft>,
policy: &'a ValidationPolicy,
) -> Self::ValidateFut<'a>;
}
pub trait DynValidator: Send + Sync {
fn validate<'a>(
&'a self,
proposal: &'a Proposal<Draft>,
policy: &'a ValidationPolicy,
) -> BoxFuture<'a, Result<ValidationReport, ValidatorError>>;
}
impl<T: Validator> DynValidator for T {
fn validate<'a>(
&'a self,
proposal: &'a Proposal<Draft>,
policy: &'a ValidationPolicy,
) -> BoxFuture<'a, Result<ValidationReport, ValidatorError>> {
Box::pin(Validator::validate(self, proposal, policy))
}
}