use molframe_chem::{BondReach, Selected, TableError, TableStore};
use molframe_core::ExecutionContext;
use molframe_core::diagnostic::{Code, Diagnostic, Diagnostics, Findings};
use molframe_core::io::ReadOptions;
use molframe_core::structure::{CoordinateStore, Structure as CoreStructure};
pub(super) fn enrich_read(
result: Result<(CoreStructure, Vec<Diagnostic>), Findings>,
options: &ReadOptions,
context: &ExecutionContext,
) -> Result<(CoreStructure, Vec<Diagnostic>), Findings> {
enrich_read_with(result, options, context, &TableStore::global())
}
pub(super) fn enrich_read_with(
result: Result<(CoreStructure, Vec<Diagnostic>), Findings>,
options: &ReadOptions,
context: &ExecutionContext,
store: &TableStore,
) -> Result<(CoreStructure, Vec<Diagnostic>), Findings> {
let (structure, mut findings) = result?;
if options.only_atomic_coords || options.perception == molframe_core::io::Perception::Skip {
return Ok((structure, findings));
}
let reach = store.select_optional::<BondReach>("--bond-reach");
let structure = match structure.ragged_models() {
Some(models) => {
let mut enriched = Vec::with_capacity(models.len());
for model in models {
enriched.push(enrich_model(model.clone(), &reach, &mut findings, context)?);
}
let mut data = structure.data().clone();
data.coords = CoordinateStore::Ragged { models: enriched };
CoreStructure::new(data)
}
None => enrich_model(structure, &reach, &mut findings, context)?,
};
let mut ordered = Diagnostics::new();
ordered.extend(findings);
let failed = ordered.has_error(options.mode.strictness());
let findings = ordered.finish();
if failed {
return Err(Findings::from(findings));
}
Ok((structure, findings))
}
fn enrich_model(
structure: CoreStructure,
reach: &Result<Option<Selected<BondReach>>, TableError>,
findings: &mut Vec<Diagnostic>,
context: &ExecutionContext,
) -> Result<CoreStructure, Findings> {
let structure = match reach {
Ok(Some(reach)) => {
match molframe_chem::perceive_bonds_in(&structure, &reach.provided.table, context) {
Ok(structure) => structure,
Err(finding)
if matches!(finding.code(), Code::E1902 | Code::E1904 | Code::E7001) =>
{
return Err(Findings::from(finding));
}
Err(finding) => {
findings.push(finding);
structure
}
}
}
Ok(None) => {
findings.push(Diagnostic::new(Code::W3207));
structure
}
Err(error) => {
findings.push(Diagnostic::from(error.clone()));
structure
}
};
let structure = match molframe_chem::annotate_standard_components(&structure) {
Ok(structure) => structure,
Err(finding) => {
findings.push(finding);
structure
}
};
let (states, sources) = merge_secondary(
structure.data(),
molframe_chem::assign_secondary_structure_in(&structure, context),
);
let mut data = structure.data().clone();
data.secondary_structure = states.into();
data.secondary_source = sources.into();
Ok(CoreStructure::new(data))
}
fn merge_secondary(
data: &molframe_core::StructureData,
inferred: Vec<molframe_core::SecondaryAssignment>,
) -> (
Vec<molframe_core::SecondaryStructure>,
Vec<molframe_core::SecondarySource>,
) {
inferred
.into_iter()
.enumerate()
.map(|(residue, inferred)| {
let current = match (
data.secondary_structure.get(residue),
data.secondary_source.get(residue),
) {
(Some(state), Some(source)) => molframe_core::SecondaryAssignment {
state: *state,
source: *source,
},
_ => molframe_core::SecondaryAssignment::default(),
};
let kept = if inferred.source.rank() > current.source.rank() {
inferred
} else {
current
};
(kept.state, kept.source)
})
.unzip()
}