348 lines
10 KiB
Rust
348 lines
10 KiB
Rust
//! 宪法条款验证模块
|
|
//!
|
|
//! 提供条款内容验证、层级验证、依赖验证和冲突检测功能
|
|
|
|
use crate::{ConstitutionalClause, ClauseTier};
|
|
use nac_udm::primitives::Hash;
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
/// 验证错误类型
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum ValidationError {
|
|
/// 条款内容为空
|
|
EmptyContent,
|
|
/// 条款标题为空
|
|
EmptyTitle,
|
|
/// 无效的条款索引
|
|
InvalidIndex,
|
|
/// 层级冲突
|
|
TierConflict(String),
|
|
/// 依赖缺失
|
|
MissingDependency(u64),
|
|
/// 循环依赖
|
|
CircularDependency(Vec<u64>),
|
|
/// 条款冲突
|
|
ClauseConflict(u64, String),
|
|
/// 生效时间无效
|
|
InvalidEffectiveTime,
|
|
/// 哈希不匹配
|
|
HashMismatch,
|
|
}
|
|
|
|
/// 条款验证器
|
|
pub struct ClauseValidator {
|
|
/// 已知的条款索引
|
|
known_clauses: HashSet<u64>,
|
|
/// 条款依赖关系 (clause_index -> dependencies)
|
|
dependencies: HashMap<u64, Vec<u64>>,
|
|
}
|
|
|
|
impl ClauseValidator {
|
|
/// 创建新的验证器
|
|
pub fn new() -> Self {
|
|
Self {
|
|
known_clauses: HashSet::new(),
|
|
dependencies: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// 注册已知条款
|
|
pub fn register_clause(&mut self, index: u64, dependencies: Vec<u64>) {
|
|
self.known_clauses.insert(index);
|
|
if !dependencies.is_empty() {
|
|
self.dependencies.insert(index, dependencies);
|
|
}
|
|
}
|
|
|
|
/// 验证条款基本内容
|
|
pub fn validate_content(&self, clause: &ConstitutionalClause) -> Result<(), ValidationError> {
|
|
// 验证标题
|
|
if clause.title.trim().is_empty() {
|
|
return Err(ValidationError::EmptyTitle);
|
|
}
|
|
|
|
// 验证内容
|
|
if clause.content.trim().is_empty() {
|
|
return Err(ValidationError::EmptyContent);
|
|
}
|
|
|
|
// 验证索引
|
|
if clause.clause_index == 0 {
|
|
return Err(ValidationError::InvalidIndex);
|
|
}
|
|
|
|
// 验证生效时间
|
|
if clause.effective_from == 0 {
|
|
return Err(ValidationError::InvalidEffectiveTime);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 验证条款层级
|
|
pub fn validate_tier(&self, clause: &ConstitutionalClause) -> Result<(), ValidationError> {
|
|
// 永恒级条款的特殊规则
|
|
if clause.tier == ClauseTier::Eternal {
|
|
// 永恒级条款索引应该在1-100范围内
|
|
if clause.clause_index > 100 {
|
|
return Err(ValidationError::TierConflict(
|
|
"永恒级条款索引应在1-100范围内".to_string()
|
|
));
|
|
}
|
|
}
|
|
|
|
// 战略级条款的特殊规则
|
|
if clause.tier == ClauseTier::Strategic {
|
|
// 战略级条款索引应该在101-1000范围内
|
|
if clause.clause_index <= 100 || clause.clause_index > 1000 {
|
|
return Err(ValidationError::TierConflict(
|
|
"战略级条款索引应在101-1000范围内".to_string()
|
|
));
|
|
}
|
|
}
|
|
|
|
// 战术级条款的特殊规则
|
|
if clause.tier == ClauseTier::Tactical {
|
|
// 战术级条款索引应该大于1000
|
|
if clause.clause_index <= 1000 {
|
|
return Err(ValidationError::TierConflict(
|
|
"战术级条款索引应大于1000".to_string()
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 验证条款依赖
|
|
pub fn validate_dependencies(&self, clause_index: u64) -> Result<(), ValidationError> {
|
|
if let Some(deps) = self.dependencies.get(&clause_index) {
|
|
// 检查所有依赖是否存在
|
|
for &dep in deps {
|
|
if !self.known_clauses.contains(&dep) {
|
|
return Err(ValidationError::MissingDependency(dep));
|
|
}
|
|
}
|
|
|
|
// 检查循环依赖
|
|
if let Some(cycle) = self.detect_circular_dependency(clause_index) {
|
|
return Err(ValidationError::CircularDependency(cycle));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 检测循环依赖
|
|
fn detect_circular_dependency(&self, start: u64) -> Option<Vec<u64>> {
|
|
let mut visited = HashSet::new();
|
|
let mut path = Vec::new();
|
|
|
|
if self.dfs_cycle_detection(start, &mut visited, &mut path) {
|
|
Some(path)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// 深度优先搜索检测循环
|
|
fn dfs_cycle_detection(&self, current: u64, visited: &mut HashSet<u64>, path: &mut Vec<u64>) -> bool {
|
|
if path.contains(¤t) {
|
|
path.push(current);
|
|
return true;
|
|
}
|
|
|
|
if visited.contains(¤t) {
|
|
return false;
|
|
}
|
|
|
|
visited.insert(current);
|
|
path.push(current);
|
|
|
|
if let Some(deps) = self.dependencies.get(¤t) {
|
|
for &dep in deps {
|
|
if self.dfs_cycle_detection(dep, visited, path) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
path.pop();
|
|
false
|
|
}
|
|
|
|
/// 验证条款哈希
|
|
pub fn validate_hash(&self, clause: &ConstitutionalClause) -> Result<(), ValidationError> {
|
|
let computed_hash = Self::compute_clause_hash(clause);
|
|
|
|
if computed_hash != clause.clause_hash {
|
|
return Err(ValidationError::HashMismatch);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 计算条款哈希
|
|
pub fn compute_clause_hash(clause: &ConstitutionalClause) -> Hash {
|
|
use sha3::{Sha3_384, Digest};
|
|
|
|
let mut hasher = Sha3_384::new();
|
|
hasher.update(clause.clause_index.to_le_bytes());
|
|
hasher.update(clause.title.as_bytes());
|
|
hasher.update(clause.content.as_bytes());
|
|
hasher.update(&[clause.tier as u8]);
|
|
hasher.update(clause.effective_from.to_le_bytes());
|
|
|
|
let result = hasher.finalize();
|
|
let mut bytes = [0u8; 48];
|
|
bytes.copy_from_slice(&result);
|
|
Hash::new(bytes)
|
|
}
|
|
|
|
/// 完整验证条款
|
|
pub fn validate_clause(&self, clause: &ConstitutionalClause) -> Result<(), ValidationError> {
|
|
self.validate_content(clause)?;
|
|
self.validate_tier(clause)?;
|
|
self.validate_dependencies(clause.clause_index)?;
|
|
self.validate_hash(clause)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Default for ClauseValidator {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn create_test_clause(index: u64, tier: ClauseTier) -> ConstitutionalClause {
|
|
let clause = ConstitutionalClause {
|
|
clause_index: index,
|
|
title: "测试条款".to_string(),
|
|
content: "这是一个测试条款内容".to_string(),
|
|
clause_hash: Hash::zero(),
|
|
effective_from: 1000,
|
|
tier,
|
|
};
|
|
|
|
let hash = ClauseValidator::compute_clause_hash(&clause);
|
|
ConstitutionalClause {
|
|
clause_hash: hash,
|
|
..clause
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_content() {
|
|
let validator = ClauseValidator::new();
|
|
let clause = create_test_clause(1, ClauseTier::Eternal);
|
|
|
|
assert!(validator.validate_content(&clause).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_empty_title() {
|
|
let validator = ClauseValidator::new();
|
|
let mut clause = create_test_clause(1, ClauseTier::Eternal);
|
|
clause.title = "".to_string();
|
|
|
|
assert_eq!(
|
|
validator.validate_content(&clause),
|
|
Err(ValidationError::EmptyTitle)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_empty_content() {
|
|
let validator = ClauseValidator::new();
|
|
let mut clause = create_test_clause(1, ClauseTier::Eternal);
|
|
clause.content = "".to_string();
|
|
|
|
assert_eq!(
|
|
validator.validate_content(&clause),
|
|
Err(ValidationError::EmptyContent)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_tier_eternal() {
|
|
let validator = ClauseValidator::new();
|
|
let clause = create_test_clause(50, ClauseTier::Eternal);
|
|
|
|
assert!(validator.validate_tier(&clause).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_tier_strategic() {
|
|
let validator = ClauseValidator::new();
|
|
let clause = create_test_clause(500, ClauseTier::Strategic);
|
|
|
|
assert!(validator.validate_tier(&clause).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_tier_tactical() {
|
|
let validator = ClauseValidator::new();
|
|
let clause = create_test_clause(2000, ClauseTier::Tactical);
|
|
|
|
assert!(validator.validate_tier(&clause).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_dependencies() {
|
|
let mut validator = ClauseValidator::new();
|
|
validator.register_clause(1, vec![]);
|
|
validator.register_clause(2, vec![1]);
|
|
|
|
assert!(validator.validate_dependencies(2).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_missing_dependency() {
|
|
let mut validator = ClauseValidator::new();
|
|
validator.register_clause(2, vec![1]);
|
|
|
|
assert_eq!(
|
|
validator.validate_dependencies(2),
|
|
Err(ValidationError::MissingDependency(1))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_circular_dependency() {
|
|
let mut validator = ClauseValidator::new();
|
|
validator.register_clause(1, vec![2]);
|
|
validator.register_clause(2, vec![1]);
|
|
|
|
assert!(matches!(
|
|
validator.validate_dependencies(1),
|
|
Err(ValidationError::CircularDependency(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_hash() {
|
|
let validator = ClauseValidator::new();
|
|
let clause = create_test_clause(1, ClauseTier::Eternal);
|
|
|
|
assert!(validator.validate_hash(&clause).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_validate_hash_mismatch() {
|
|
let validator = ClauseValidator::new();
|
|
let mut clause = create_test_clause(1, ClauseTier::Eternal);
|
|
clause.clause_hash = Hash::zero();
|
|
|
|
assert_eq!(
|
|
validator.validate_hash(&clause),
|
|
Err(ValidationError::HashMismatch)
|
|
);
|
|
}
|
|
}
|