use std::collections::HashMap;
use std::time::SystemTime;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Role {
Dev,
Qa,
System,
}
impl Role {
pub fn name(&self) -> &'static str {
match self {
Role::Dev => "Developer",
Role::Qa => "QA",
Role::System => "System",
}
}
pub fn can_claim(&self) -> bool {
matches!(self, Role::Dev)
}
pub fn can_verify(&self) -> bool {
matches!(self, Role::Qa)
}
pub fn can_approve(&self) -> bool {
matches!(self, Role::System)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerificationResult {
Falsified,
Unfalsified,
Inconclusive,
}
impl VerificationResult {
pub fn should_approve(&self) -> bool {
matches!(self, VerificationResult::Unfalsified)
}
}
#[derive(Debug, Clone)]
pub struct FalsificationCriterion {
pub id: String,
pub description: String,
pub pass_condition: String,
}
impl FalsificationCriterion {
pub fn new(id: &str, description: &str, pass_condition: &str) -> Self {
Self {
id: id.to_string(),
description: description.to_string(),
pass_condition: pass_condition.to_string(),
}
}
pub fn hash(&self) -> u64 {
let mut hash: u64 = 0;
for byte in self.id.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
}
for byte in self.description.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
}
for byte in self.pass_condition.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
}
hash
}
}
#[derive(Debug, Clone)]
pub struct FalsificationClaim {
pub id: String,
pub feature: String,
pub criteria: Vec<FalsificationCriterion>,
pub criteria_hash: u64,
pub timestamp: SystemTime,
pub claimant: String,
pub version: String,
pub evidence: Vec<String>,
}
impl FalsificationClaim {
pub fn new(id: &str, feature: &str, claimant: &str, version: &str) -> Self {
Self {
id: id.to_string(),
feature: feature.to_string(),
criteria: Vec::new(),
criteria_hash: 0,
timestamp: SystemTime::now(),
claimant: claimant.to_string(),
version: version.to_string(),
evidence: Vec::new(),
}
}
pub fn add_criterion(&mut self, criterion: FalsificationCriterion) {
self.criteria.push(criterion);
self.update_hash();
}
pub fn add_evidence(&mut self, evidence: &str) {
self.evidence.push(evidence.to_string());
}
fn update_hash(&mut self) {
let mut hash: u64 = 0;
for criterion in &self.criteria {
hash = hash.wrapping_add(criterion.hash());
}
self.criteria_hash = hash;
}
pub fn verify_hash(&self) -> bool {
let mut expected: u64 = 0;
for criterion in &self.criteria {
expected = expected.wrapping_add(criterion.hash());
}
expected == self.criteria_hash
}
pub fn is_valid(&self) -> bool {
!self.id.is_empty()
&& !self.feature.is_empty()
&& !self.claimant.is_empty()
&& !self.version.is_empty()
&& !self.criteria.is_empty()
&& self.verify_hash()
}
}
#[derive(Debug, Clone)]
pub struct BlackBoxArtifact {
pub id: String,
pub binary_hash: String,
pub criteria: Vec<FalsificationCriterion>,
pub criteria_hash: u64,
pub version: String,
pub deadline: Option<SystemTime>,
}
impl BlackBoxArtifact {
pub fn from_claim(claim: &FalsificationClaim, binary_hash: &str) -> Self {
Self {
id: format!("BB-{}", claim.id),
binary_hash: binary_hash.to_string(),
criteria: claim.criteria.clone(),
criteria_hash: claim.criteria_hash,
version: claim.version.clone(),
deadline: None,
}
}
pub fn with_deadline(mut self, deadline: SystemTime) -> Self {
self.deadline = Some(deadline);
self
}
pub fn is_expired(&self) -> bool {
if let Some(deadline) = self.deadline {
SystemTime::now() > deadline
} else {
false
}
}
pub fn verify_criteria_integrity(&self, claim: &FalsificationClaim) -> bool {
self.criteria_hash == claim.criteria_hash
}
}
#[derive(Debug, Clone)]
pub struct VerificationAttempt {
pub id: String,
pub artifact_id: String,
pub verifier: String,
pub result: VerificationResult,
pub timestamp: SystemTime,
pub evidence: Vec<String>,
pub criterion_results: HashMap<String, bool>,
}
impl VerificationAttempt {
pub fn new(id: &str, artifact_id: &str, verifier: &str) -> Self {
Self {
id: id.to_string(),
artifact_id: artifact_id.to_string(),
verifier: verifier.to_string(),
result: VerificationResult::Inconclusive,
timestamp: SystemTime::now(),
evidence: Vec::new(),
criterion_results: HashMap::new(),
}
}
pub fn record_criterion(&mut self, criterion_id: &str, passed: bool) {
self.criterion_results
.insert(criterion_id.to_string(), passed);
}
pub fn add_evidence(&mut self, evidence: &str) {
self.evidence.push(evidence.to_string());
}
pub fn finalize(&mut self, result: VerificationResult) {
self.result = result;
self.timestamp = SystemTime::now();
}
pub fn has_falsification(&self) -> bool {
self.criterion_results.values().any(|&passed| !passed)
}
pub fn passed_count(&self) -> usize {
self.criterion_results.values().filter(|&&p| p).count()
}
pub fn failed_count(&self) -> usize {
self.criterion_results.values().filter(|&&p| !p).count()
}
}
#[derive(Debug, Clone)]
pub struct ScorecardComponent {
pub name: String,
pub weight: f64,
pub score: u32,
}
impl ScorecardComponent {
pub fn new(name: &str, weight: f64, score: u32) -> Self {
Self {
name: name.to_string(),
weight,
score: score.min(100),
}
}
pub fn weighted_score(&self) -> f64 {
self.weight * f64::from(self.score)
}
}
#[derive(Debug, Clone)]
pub struct ScorecardV2 {
pub components: Vec<ScorecardComponent>,
pub version: u8,
}
impl Default for ScorecardV2 {
fn default() -> Self {
Self::new()
}
}
impl ScorecardV2 {
pub fn new() -> Self {
Self {
components: vec![
ScorecardComponent::new("Core Correctness", 0.30, 0),
ScorecardComponent::new("Performance", 0.30, 0),
ScorecardComponent::new("Resilience", 0.20, 0),
ScorecardComponent::new("Usability", 0.20, 0),
],
version: 2,
}
}
pub fn set_score(&mut self, name: &str, score: u32) -> bool {
for component in &mut self.components {
if component.name == name {
component.score = score.min(100);
return true;
}
}
false
}
pub fn total_score(&self) -> f64 {
self.components.iter().map(|c| c.weighted_score()).sum()
}
pub fn weights_valid(&self) -> bool {
let sum: f64 = self.components.iter().map(|c| c.weight).sum();
(sum - 1.0).abs() < 1e-10
}
pub fn passes(&self) -> bool {
self.total_score() >= 70.0
}
pub fn grade(&self) -> &'static str {
let score = self.total_score();
if score >= 90.0 {
"A"
} else if score >= 80.0 {
"B"
} else if score >= 70.0 {
"C"
} else if score >= 60.0 {
"D"
} else {
"F"
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReleaseDecision {
Approved { reason: String },
Rejected { reason: String },
Pending { reason: String },
}
impl ReleaseDecision {
pub fn is_approved(&self) -> bool {
matches!(self, ReleaseDecision::Approved { .. })
}
pub fn reason(&self) -> &str {
match self {
ReleaseDecision::Approved { reason }
| ReleaseDecision::Rejected { reason }
| ReleaseDecision::Pending { reason } => reason,
}
}
}
#[derive(Debug, Clone)]
pub struct AuditEntry {
pub id: String,
pub timestamp: SystemTime,
pub role: Role,
pub actor: String,
pub action: String,
pub artifacts: Vec<String>,
}
impl AuditEntry {
pub fn new(id: &str, role: Role, actor: &str, action: &str) -> Self {
Self {
id: id.to_string(),
timestamp: SystemTime::now(),
role,
actor: actor.to_string(),
action: action.to_string(),
artifacts: Vec::new(),
}
}
pub fn with_artifact(mut self, artifact_id: &str) -> Self {
self.artifacts.push(artifact_id.to_string());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionState {
AwaitingClaims,
AwaitingVerification,
AwaitingDecision,
Completed,
}
#[derive(Debug, Clone)]
pub struct VerificationReport {
pub session_id: String,
pub total_claims: usize,
pub total_artifacts: usize,
pub total_attempts: usize,
pub falsified_count: usize,
pub unfalsified_count: usize,
pub inconclusive_count: usize,
pub scorecard_total: f64,
pub scorecard_grade: String,
pub audit_entries: usize,
}
impl VerificationReport {
pub fn is_success(&self) -> bool {
self.falsified_count == 0 && self.scorecard_total >= 70.0
}
}