molframe_core/contract/policy/
consistency.rs1use super::fields::{AssemblyChoice, PolicyField, SymmetryPolicy};
11use super::profile::AnalysisPolicy;
12use crate::diagnostic::{Code, Diagnostic};
13
14impl AnalysisPolicy {
15 pub fn check_consistency(&self) -> Result<(), Diagnostic> {
31 let conflict = |reason: &str| {
32 Err(Diagnostic::new(Code::E6004)
33 .with_context(
34 "fields",
35 format!(
36 "{}, {}",
37 PolicyField::Assembly.name(),
38 PolicyField::Symmetry.name()
39 ),
40 )
41 .with_context("assembly", assembly_name(&self.assembly))
42 .with_context("symmetry", symmetry_name(self.symmetry))
43 .with_context("reason", reason))
44 };
45 match (&self.assembly, self.symmetry) {
46 (AssemblyChoice::AsymmetricUnit, SymmetryPolicy::None)
47 | (
48 AssemblyChoice::Biological(_),
49 SymmetryPolicy::None | SymmetryPolicy::BiologicalAssembly,
50 ) => Ok(()),
51 (
52 AssemblyChoice::Crystal { radius },
53 SymmetryPolicy::None | SymmetryPolicy::Crystallographic,
54 ) => {
55 if radius.is_finite() && *radius > 0.0 {
56 Ok(())
57 } else {
58 Err(Diagnostic::new(Code::E6016)
59 .with_context("radius", radius.to_string())
60 .with_context("reason", "a crystal radius must be positive and finite"))
61 }
62 }
63 (AssemblyChoice::AsymmetricUnit, SymmetryPolicy::Crystallographic) => conflict(
64 "crystallographic copies need AssemblyChoice::Crystal, whose radius bounds them",
65 ),
66 (AssemblyChoice::AsymmetricUnit, SymmetryPolicy::BiologicalAssembly) => conflict(
67 "biological-assembly copies need AssemblyChoice::Biological, which names the assembly",
68 ),
69 (AssemblyChoice::Biological(_), SymmetryPolicy::Crystallographic) => {
70 conflict("a biological assembly is not generated by crystal symmetry")
71 }
72 (AssemblyChoice::Crystal { .. }, SymmetryPolicy::BiologicalAssembly) => {
73 conflict("crystal contacts are not a biological assembly")
74 }
75 }
76 }
77}
78
79fn assembly_name(choice: &AssemblyChoice) -> String {
80 match choice {
81 AssemblyChoice::AsymmetricUnit => "asymmetric-unit".to_owned(),
82 AssemblyChoice::Biological(id) => format!("biological:{id}"),
83 AssemblyChoice::Crystal { radius } => format!("crystal:{radius}"),
84 }
85}
86
87const fn symmetry_name(policy: SymmetryPolicy) -> &'static str {
88 match policy {
89 SymmetryPolicy::None => "none",
90 SymmetryPolicy::Crystallographic => "crystallographic",
91 SymmetryPolicy::BiologicalAssembly => "biological-assembly",
92 }
93}
94
95#[cfg(test)]
96#[path = "consistency_tests.rs"]
97mod tests;