pub mod config_contracts {
pub fn validate_size(size: usize, max: usize) -> bool {
size <= max
}
pub fn validate_index(index: usize, len: usize) -> bool {
index < len
}
pub fn validated_len(data: &[u8]) -> usize {
debug_assert!(!data.is_empty(), "data must not be empty");
data.len()
}
}
pub mod numeric_contracts {
pub fn checked_add(a: u64, b: u64) -> Option<u64> {
a.checked_add(b)
}
pub fn is_valid_float(val: f64) -> bool {
val.is_finite()
}
pub fn normalize(val: f64, min: f64, max: f64) -> f64 {
debug_assert!(max > min, "max must be greater than min");
((val - min) / (max - min)).clamp(0.0, 1.0)
}
}
#[cfg(verus)]
mod verus_specs {
use builtin::*;
use builtin_macros::*;
verus! {
#[requires(dep_index < total_deps)]
#[ensures(result == dep_index)]
fn verify_dep_order_index(dep_index: u64, total_deps: u64) -> u64 { dep_index }
#[requires(num_crates > 0)]
#[ensures(result <= num_crates)]
#[invariant(published <= num_crates)]
fn verify_publish_progress(published: u64, num_crates: u64) -> u64 { published }
#[requires(order_len > 0)]
#[ensures(result == (position < order_len))]
#[decreases(order_len - position)]
fn verify_topo_position(position: u64, order_len: u64) -> bool {
position < order_len
}
#[requires(major <= 999 && minor <= 999 && patch <= 999)]
#[ensures(result > 0)]
fn verify_semver_encoding(major: u64, minor: u64, patch: u64) -> u64 {
major * 1_000_000 + minor * 1_000 + patch + 1
}
#[requires(old_version > 0)]
#[ensures(result > old_version)]
#[recommends(result == old_version + 1)]
fn verify_version_bump(old_version: u64, new_version: u64) -> u64 {
new_version
}
#[ensures(result == (new_major > old_major
|| (new_major == old_major && new_minor > old_minor)
|| (new_major == old_major && new_minor == old_minor && new_patch > old_patch)))]
fn verify_version_greater(
old_major: u64, old_minor: u64, old_patch: u64,
new_major: u64, new_minor: u64, new_patch: u64,
) -> bool {
new_major > old_major
|| (new_major == old_major && new_minor > old_minor)
|| (new_major == old_major && new_minor == old_minor && new_patch > old_patch)
}
#[requires(direct_deps >= 0)]
#[ensures(result >= direct_deps)]
#[recommends(direct_deps <= 50)]
fn verify_dep_count(direct_deps: u64, transitive_deps: u64) -> u64 {
direct_deps + transitive_deps
}
#[requires(total > 0)]
#[ensures(result <= 100)]
fn verify_dep_ratio(outdated: u64, total: u64) -> u64 {
(outdated * 100) / total
}
#[requires(num_crates > 0)]
#[ensures(result == (succeeded == num_crates))]
#[invariant(succeeded <= num_crates)]
fn verify_release_complete(succeeded: u64, num_crates: u64) -> bool {
succeeded == num_crates
}
#[requires(crate_index < stack_size)]
#[ensures(result == true)]
#[recommends(stack_size <= 10)]
fn verify_crate_in_stack(crate_index: u64, stack_size: u64) -> bool {
crate_index < stack_size
}
#[ensures(result == (actual_major == expected_major && actual_minor == expected_minor))]
fn verify_no_version_drift(
actual_major: u64, actual_minor: u64,
expected_major: u64, expected_minor: u64,
) -> bool {
actual_major == expected_major && actual_minor == expected_minor
}
#[requires(drift_count >= 0)]
#[ensures(result == (drift_count == 0))]
#[recommends(drift_count == 0)]
fn verify_zero_drift(drift_count: u64) -> bool {
drift_count == 0
}
#[requires(num_crates > 0)]
#[ensures(result == (denied == 0))]
fn verify_license_compliance(denied: u64) -> bool {
denied == 0
}
#[requires(edges >= 0)]
#[ensures(result == (edges < nodes))]
#[invariant(nodes > 0)]
fn verify_dag_acyclic_hint(nodes: u64, edges: u64) -> bool {
edges < nodes
}
#[requires(in_degree >= 0)]
#[ensures(result == (in_degree == 0))]
#[decreases(remaining_nodes)]
fn verify_topo_sort_ready(in_degree: u64, remaining_nodes: u64) -> bool {
in_degree == 0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_size() {
assert!(config_contracts::validate_size(5, 10));
assert!(!config_contracts::validate_size(11, 10));
assert!(config_contracts::validate_size(10, 10));
}
#[test]
fn test_validate_index() {
assert!(config_contracts::validate_index(0, 5));
assert!(config_contracts::validate_index(4, 5));
assert!(!config_contracts::validate_index(5, 5));
}
#[test]
fn test_validated_len() {
assert_eq!(config_contracts::validated_len(&[1, 2, 3]), 3);
}
#[test]
fn test_checked_add() {
assert_eq!(numeric_contracts::checked_add(1, 2), Some(3));
assert_eq!(numeric_contracts::checked_add(u64::MAX, 1), None);
}
#[test]
fn test_is_valid_float() {
assert!(numeric_contracts::is_valid_float(1.0));
assert!(!numeric_contracts::is_valid_float(f64::NAN));
assert!(!numeric_contracts::is_valid_float(f64::INFINITY));
}
#[test]
fn test_normalize() {
let result = numeric_contracts::normalize(5.0, 0.0, 10.0);
assert!((result - 0.5).abs() < f64::EPSILON);
assert!((numeric_contracts::normalize(0.0, 0.0, 10.0)).abs() < f64::EPSILON);
assert!((numeric_contracts::normalize(10.0, 0.0, 10.0) - 1.0).abs() < f64::EPSILON);
}
}
#[cfg(kani)]
mod kani_proofs {
#[kani::proof]
fn verify_config_bounds() {
let val: u32 = kani::any();
kani::assume(val <= 1000);
assert!(val <= 1000);
}
#[kani::proof]
fn verify_index_safety() {
let len: usize = kani::any();
kani::assume(len > 0 && len <= 1024);
let idx: usize = kani::any();
kani::assume(idx < len);
assert!(idx < len);
}
#[kani::proof]
fn verify_no_overflow_add() {
let a: u32 = kani::any();
let b: u32 = kani::any();
kani::assume(a <= 10000);
kani::assume(b <= 10000);
let result = a.checked_add(b);
assert!(result.is_some());
}
#[kani::proof]
fn verify_no_overflow_mul() {
let a: u32 = kani::any();
let b: u32 = kani::any();
kani::assume(a <= 1000);
kani::assume(b <= 1000);
let result = a.checked_mul(b);
assert!(result.is_some());
}
#[kani::proof]
fn verify_division_nonzero() {
let numerator: u64 = kani::any();
let denominator: u64 = kani::any();
kani::assume(denominator > 0);
let result = numerator / denominator;
assert!(result <= numerator);
}
}