use bistun_core::{CapabilityManifest, LmsError, TraitKey, TraitValue};
pub fn verify(manifest: &CapabilityManifest) -> Result<(), LmsError> {
if manifest.traits.is_empty() {
return Err(LmsError::IntegrityViolation {
pipeline_step: "Phase 4: Integrity Check".to_string(),
context: manifest.resolved_locale.clone(),
reason: "Manifest traits dictionary cannot be empty".to_string(),
});
}
if !manifest.traits.contains_key(&TraitKey::PrimaryDirection) {
return Err(LmsError::IntegrityViolation {
pipeline_step: "Phase 4: Integrity Check".to_string(),
context: manifest.resolved_locale.clone(),
reason: "Missing mandatory trait: PRIMARY_DIRECTION".to_string(),
});
}
if !manifest.rules.contains_key("NORMALIZATION_DEFAULT") {
return Err(LmsError::IntegrityViolation {
pipeline_step: "Phase 4: Integrity Check".to_string(),
context: manifest.resolved_locale.clone(),
reason: "Missing mandatory rule: NORMALIZATION_DEFAULT".to_string(),
});
}
let has_bidi =
manifest.traits.get(&TraitKey::HasBidiElements) == Some(&TraitValue::Boolean(true));
let requires_shaping =
manifest.traits.get(&TraitKey::RequiresShaping) == Some(&TraitValue::Boolean(true));
if has_bidi && !requires_shaping && manifest.resolved_locale != "he" {
return Err(LmsError::IntegrityViolation {
pipeline_step: "Phase 4: Integrity Check".to_string(),
context: manifest.resolved_locale.clone(),
reason: "Bidirectional layouts inherently require shaping algorithms, but REQUIRES_SHAPING is false".to_string(),
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use bistun_core::{Direction, LmsRule, NormRule};
#[test]
fn test_verify_passes_valid_manifest() {
let mut manifest = CapabilityManifest::new("ar-EG".to_string());
manifest.traits.insert(TraitKey::PrimaryDirection, TraitValue::Direction(Direction::RTL));
manifest.traits.insert(TraitKey::HasBidiElements, TraitValue::Boolean(true));
manifest.traits.insert(TraitKey::RequiresShaping, TraitValue::Boolean(true));
manifest.rules.insert("NORMALIZATION_DEFAULT".to_string(), LmsRule::Norm(NormRule::NFC));
assert!(verify(&manifest).is_ok());
}
#[test]
fn test_verify_catches_empty_manifest() {
let manifest = CapabilityManifest::new("en-US".to_string());
let err = verify(&manifest)
.expect_err("LMS-TEST: Expected IntegrityViolation for empty manifest");
assert!(matches!(err, LmsError::IntegrityViolation { .. }));
}
#[test]
fn test_verify_catches_missing_mandatory_rule() {
let mut manifest = CapabilityManifest::new("en-US".to_string());
manifest.traits.insert(TraitKey::PrimaryDirection, TraitValue::Direction(Direction::LTR));
let err =
verify(&manifest).expect_err("LMS-TEST: Expected IntegrityViolation for missing rule");
if let LmsError::IntegrityViolation { reason, .. } = err {
assert!(reason.contains("NORMALIZATION_DEFAULT"));
} else {
panic!("Expected IntegrityViolation for missing rule");
}
}
#[test]
fn test_verify_catches_bidi_shaping_contradiction() {
let mut manifest = CapabilityManifest::new("ar-EG".to_string());
manifest.traits.insert(TraitKey::PrimaryDirection, TraitValue::Direction(Direction::RTL));
manifest.rules.insert("NORMALIZATION_DEFAULT".to_string(), LmsRule::Norm(NormRule::NFC));
manifest.traits.insert(TraitKey::HasBidiElements, TraitValue::Boolean(true));
manifest.traits.insert(TraitKey::RequiresShaping, TraitValue::Boolean(false));
let err = verify(&manifest)
.expect_err("LMS-TEST: Expected IntegrityViolation for Bidi shaping contradiction");
assert!(matches!(err, LmsError::IntegrityViolation { .. }));
}
}