use super::common::LifecycleStatus;
use crate::text::normalize_required_text;
use crate::{CoreError, Id, Provenance, Result, ReviewStatus};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum VerifierKind {
HumanReview,
SchemaValidator,
ProofChecker,
TestRun,
StaticAnalysis,
CustomEngine,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Verifier {
pub kind: VerifierKind,
pub reference: String,
}
impl Verifier {
pub fn new(kind: VerifierKind, reference: impl Into<String>) -> Result<Self> {
Ok(Self {
kind,
reference: normalize_required_text("verifier.reference", reference)?,
})
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum VerificationStatus {
Unverified,
MachineChecked,
HumanReviewed,
Failed,
Superseded,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DerivationFailureMode {
MissingPremise,
InvalidRule,
OutOfScopeRule,
ContradictedByWitness,
CircularDerivation,
UnsupportedJump,
VerifierUnavailable,
None,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct InferenceRule {
pub id: Id,
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rule_scope_contexts: Vec<Id>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interpretation_package: Option<Id>,
}
impl InferenceRule {
pub fn new(id: Id, name: impl Into<String>) -> Result<Self> {
Ok(Self {
id,
name: normalize_required_text("inference_rule.name", name)?,
rule_scope_contexts: Vec::new(),
interpretation_package: None,
})
}
fn has_scope(&self) -> bool {
!self.rule_scope_contexts.is_empty() || self.interpretation_package.is_some()
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Derivation {
pub id: Id,
pub conclusion: Id,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub premises: Vec<Id>,
pub inference_rule: InferenceRule,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub warrants: Vec<Id>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub excluded_premises: Vec<Id>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub counterexamples: Vec<Id>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verifier: Option<Verifier>,
pub verification_status: VerificationStatus,
pub failure_mode: DerivationFailureMode,
pub provenance: Provenance,
pub review_status: LifecycleStatus,
}
impl Derivation {
pub fn candidate(
id: Id,
conclusion: Id,
premises: Vec<Id>,
inference_rule: InferenceRule,
provenance: Provenance,
) -> Self {
Self {
id,
conclusion,
premises,
inference_rule,
warrants: Vec::new(),
excluded_premises: Vec::new(),
counterexamples: Vec::new(),
verifier: None,
verification_status: VerificationStatus::Unverified,
failure_mode: DerivationFailureMode::None,
provenance,
review_status: LifecycleStatus::Candidate,
}
}
pub fn validate_acceptance(&self) -> Result<()> {
if self
.premises
.iter()
.all(|premise| premise == &self.conclusion)
{
return Err(CoreError::malformed_field(
"premises",
"derivation cannot be circular over only the conclusion",
));
}
if !self.inference_rule.has_scope() {
return Err(CoreError::malformed_field(
"inference_rule.rule_scope",
"accepted derivation requires a scoped inference rule",
));
}
if self.failure_mode != DerivationFailureMode::None {
return Err(CoreError::malformed_field(
"failure_mode",
"accepted derivation requires no unresolved failure mode",
));
}
if !self.counterexamples.is_empty() {
return Err(CoreError::malformed_field(
"counterexamples",
"accepted derivation cannot have unresolved counterexamples",
));
}
match self.verification_status {
VerificationStatus::MachineChecked | VerificationStatus::HumanReviewed => {}
_ => {
return Err(CoreError::malformed_field(
"verification_status",
"accepted derivation requires machine_checked or human_reviewed status",
));
}
}
if self.verifier.is_none() && self.provenance.review_status != ReviewStatus::Accepted {
return Err(CoreError::malformed_field(
"verifier",
"accepted derivation requires verifier or explicit accepted review",
));
}
Ok(())
}
}