#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractClause {
pub id: String,
pub kind: ClauseKind,
pub description: String,
pub falsification_method: FalsificationMethod,
pub threshold: Option<ClauseThreshold>,
pub blocking: bool,
pub source: ClauseSource,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ClauseKind {
Require,
Ensure,
Invariant,
}
impl std::fmt::Display for ClauseKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ClauseKind::Require => write!(f, "require"),
ClauseKind::Ensure => write!(f, "ensure"),
ClauseKind::Invariant => write!(f, "invariant"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClauseSource {
Default,
Inherited { from_iteration: u32 },
Manual,
Stack { manifest_name: String },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ClauseThreshold {
Numeric {
metric: String,
op: ThresholdOp,
value: f64,
},
Boolean { expected: bool },
Delta {
metric: String,
op: ThresholdOp,
value: f64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThresholdOp {
Gte,
Lte,
Eq,
Gt,
Lt,
}
impl std::fmt::Display for ThresholdOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ThresholdOp::Gte => write!(f, ">="),
ThresholdOp::Lte => write!(f, "<="),
ThresholdOp::Eq => write!(f, "=="),
ThresholdOp::Gt => write!(f, ">"),
ThresholdOp::Lt => write!(f, "<"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThresholdComparison {
Strengthened,
Weakened,
Equal,
Incompatible,
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn compare_thresholds(
parent: &Option<ClauseThreshold>,
child: &Option<ClauseThreshold>,
) -> ThresholdComparison {
match (parent, child) {
(None, None) => ThresholdComparison::Equal,
(None, Some(_)) => ThresholdComparison::Strengthened,
(Some(_), None) => ThresholdComparison::Weakened,
(Some(p), Some(c)) => compare_threshold_values(p, c),
}
}
fn compare_threshold_values(parent: &ClauseThreshold, child: &ClauseThreshold) -> ThresholdComparison {
match (parent, child) {
(
ClauseThreshold::Numeric {
op: p_op,
value: p_val,
..
},
ClauseThreshold::Numeric {
op: c_op,
value: c_val,
..
},
) => compare_numeric(*p_op, *p_val, *c_op, *c_val),
(
ClauseThreshold::Delta {
op: p_op,
value: p_val,
..
},
ClauseThreshold::Delta {
op: c_op,
value: c_val,
..
},
) => compare_numeric(*p_op, *p_val, *c_op, *c_val),
(
ClauseThreshold::Boolean { expected: p },
ClauseThreshold::Boolean { expected: c },
) => match (p, c) {
(true, true) | (false, false) => ThresholdComparison::Equal,
(false, true) => ThresholdComparison::Strengthened,
(true, false) => ThresholdComparison::Weakened,
},
_ => ThresholdComparison::Incompatible,
}
}
fn compare_numeric(
p_op: ThresholdOp,
p_val: f64,
c_op: ThresholdOp,
c_val: f64,
) -> ThresholdComparison {
if p_op != c_op {
return ThresholdComparison::Incompatible;
}
if p_op == ThresholdOp::Eq {
return if (c_val - p_val).abs() < f64::EPSILON {
ThresholdComparison::Equal
} else {
ThresholdComparison::Incompatible
};
}
let higher_is_stronger = matches!(p_op, ThresholdOp::Gte | ThresholdOp::Gt);
compare_ordered(c_val, p_val, higher_is_stronger)
}
fn compare_ordered(child: f64, parent: f64, higher_is_stronger: bool) -> ThresholdComparison {
#[allow(clippy::float_cmp)]
if child == parent {
return ThresholdComparison::Equal;
}
let child_higher = child > parent;
if child_higher == higher_is_stronger {
ThresholdComparison::Strengthened
} else {
ThresholdComparison::Weakened
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExcludedClaim {
pub id: String,
pub reason: String,
pub flag: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractQuality {
pub active_claims: usize,
pub applicable_claims: usize,
pub score: f64,
pub rating: String,
}
impl ContractQuality {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "score_range")]
pub fn calculate(active: usize, applicable: usize) -> Self {
let score = if applicable == 0 {
0.0
} else {
active as f64 / applicable as f64
};
let rating = Self::rate(score);
Self { active_claims: active, applicable_claims: applicable, score, rating }
}
fn rate(score: f64) -> String {
if score >= 1.0 { "Full" }
else if score >= 0.8 { "Strong" }
else if score >= 0.5 { "Partial" }
else { "Weak" }
.to_string()
}
}
#[derive(Debug, Clone)]
pub enum SubcontractingViolation {
PostconditionDropped { clause: String },
PostconditionWeakened {
clause: String,
parent_threshold: Option<ClauseThreshold>,
child_threshold: Option<ClauseThreshold>,
},
IncompatibleThresholds {
clause: String,
parent_threshold: Option<ClauseThreshold>,
child_threshold: Option<ClauseThreshold>,
},
}
impl std::fmt::Display for SubcontractingViolation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SubcontractingViolation::PostconditionDropped { clause } => {
write!(f, "postcondition dropped: {clause}")
}
SubcontractingViolation::PostconditionWeakened { clause, .. } => {
write!(f, "postcondition weakened: {clause}")
}
SubcontractingViolation::IncompatibleThresholds { clause, .. } => {
write!(f, "incompatible thresholds: {clause}")
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckpointRecord {
pub checkpoint_id: String,
pub work_item_id: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub git_sha: String,
pub iteration: u32,
pub invariant_results: Vec<InvariantResult>,
pub all_invariants_hold: bool,
#[serde(default)]
pub drift_bound: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvariantResult {
pub clause_id: String,
pub passed: bool,
pub explanation: String,
}
impl CheckpointRecord {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(
work_item_id: String,
git_sha: String,
iteration: u32,
invariant_results: Vec<InvariantResult>,
) -> Self {
let all_hold = invariant_results.iter().all(|r| r.passed);
Self {
checkpoint_id: uuid::Uuid::new_v4().to_string(),
work_item_id,
timestamp: chrono::Utc::now(),
git_sha,
iteration,
invariant_results,
all_invariants_hold: all_hold,
drift_bound: None,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn save(&self, project_path: &Path) -> Result<PathBuf> {
let checkpoint_dir = project_path
.join(".pmat-work")
.join(&self.work_item_id)
.join("checkpoints");
std::fs::create_dir_all(&checkpoint_dir)?;
let filename = format!("checkpoint-{}.json", self.checkpoint_id);
let checkpoint_path = checkpoint_dir.join(filename);
let json = serde_json::to_string_pretty(self)?;
std::fs::write(&checkpoint_path, &json)?;
Ok(checkpoint_path)
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn load_all(project_path: &Path, work_item_id: &str) -> Vec<Self> {
let checkpoint_dir = project_path
.join(".pmat-work")
.join(work_item_id)
.join("checkpoints");
let mut records = Vec::new();
if let Ok(entries) = std::fs::read_dir(&checkpoint_dir) {
for entry in entries.flatten() {
if let Ok(content) = std::fs::read_to_string(entry.path()) {
if let Ok(record) = serde_json::from_str::<CheckpointRecord>(&content) {
records.push(record);
}
}
}
}
records.sort_by_key(|r| r.timestamp);
records
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn validate_subcontracting(
parent_ensure: &[ContractClause],
child_ensure: &[ContractClause],
) -> Result<(), SubcontractingViolation> {
for parent_clause in parent_ensure {
let child_clause = child_ensure.iter().find(|c| c.id == parent_clause.id);
match child_clause {
None => {
return Err(SubcontractingViolation::PostconditionDropped {
clause: parent_clause.id.clone(),
});
}
Some(child) => {
match compare_thresholds(&parent_clause.threshold, &child.threshold) {
ThresholdComparison::Weakened => {
return Err(SubcontractingViolation::PostconditionWeakened {
clause: parent_clause.id.clone(),
parent_threshold: parent_clause.threshold.clone(),
child_threshold: child.threshold.clone(),
});
}
ThresholdComparison::Incompatible => {
return Err(SubcontractingViolation::IncompatibleThresholds {
clause: parent_clause.id.clone(),
parent_threshold: parent_clause.threshold.clone(),
child_threshold: child.threshold.clone(),
});
}
ThresholdComparison::Strengthened | ThresholdComparison::Equal => {}
}
}
}
}
Ok(())
}