use super::analysis::{Coverage, Status};
use super::policy::MissingPolicy;
use std::fmt;
#[derive(Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum Indeterminacy {
MissingInputs {
missing: u32,
ambiguous: u32,
},
UnresolvedConformations,
Frame {
frame: usize,
reason: Box<Indeterminacy>,
},
Other(Box<str>),
}
impl fmt::Display for Indeterminacy {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingInputs { missing, ambiguous } => write!(
formatter,
"the policy rejects {missing} missing and {ambiguous} ambiguous inputs"
),
Self::UnresolvedConformations => {
formatter.write_str("the alternate-conformation rule selected no consistent atoms")
}
Self::Frame { frame, reason } => write!(formatter, "frame {frame}: {reason}"),
Self::Other(reason) => formatter.write_str(reason),
}
}
}
impl std::error::Error for Indeterminacy {}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Outcome<T> {
Determinate(T),
Indeterminate(Indeterminacy),
}
impl<T> Outcome<T> {
#[must_use]
pub const fn is_determinate(&self) -> bool {
matches!(self, Self::Determinate(_))
}
#[must_use]
pub const fn value(&self) -> Option<&T> {
match self {
Self::Determinate(value) => Some(value),
Self::Indeterminate(_) => None,
}
}
#[must_use]
pub const fn indeterminacy(&self) -> Option<&Indeterminacy> {
match self {
Self::Determinate(_) => None,
Self::Indeterminate(reason) => Some(reason),
}
}
pub fn into_result(self) -> Result<T, Indeterminacy> {
match self {
Self::Determinate(value) => Ok(value),
Self::Indeterminate(reason) => Err(reason),
}
}
pub const fn as_result(&self) -> Result<&T, &Indeterminacy> {
match self {
Self::Determinate(value) => Ok(value),
Self::Indeterminate(reason) => Err(reason),
}
}
pub fn map<U>(self, transform: impl FnOnce(T) -> U) -> Outcome<U> {
match self {
Self::Determinate(value) => Outcome::Determinate(transform(value)),
Self::Indeterminate(reason) => Outcome::Indeterminate(reason),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum Quality {
#[default]
Complete,
Partial,
Ambiguous,
}
impl Quality {
#[must_use]
pub fn worst(self, other: Self) -> Self {
match (self, other) {
(Self::Ambiguous, _) | (_, Self::Ambiguous) => Self::Ambiguous,
(Self::Partial, _) | (_, Self::Partial) => Self::Partial,
(Self::Complete, Self::Complete) => Self::Complete,
}
}
}
impl From<Quality> for Status {
fn from(quality: Quality) -> Self {
match quality {
Quality::Complete => Self::Complete,
Quality::Partial => Self::Partial,
Quality::Ambiguous => Self::Ambiguous,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum MissingPolicyError {
Fail {
missing: u32,
ambiguous: u32,
},
}
impl fmt::Display for MissingPolicyError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fail { missing, ambiguous } => write!(
formatter,
"the policy fails on {missing} missing and {ambiguous} ambiguous inputs"
),
}
}
}
impl std::error::Error for MissingPolicyError {}
pub fn resolve_missing<T>(
value: T,
quality: Quality,
coverage: Coverage,
policy: MissingPolicy,
) -> Result<(Outcome<T>, Quality), MissingPolicyError> {
if coverage.missing == 0 && coverage.ambiguous == 0 {
return Ok((Outcome::Determinate(value), quality));
}
match policy {
MissingPolicy::Ignore => Ok((Outcome::Determinate(value), quality)),
MissingPolicy::Report => Ok((Outcome::Determinate(value), quality.worst(Quality::Partial))),
MissingPolicy::Indeterminate => Ok((
Outcome::Indeterminate(Indeterminacy::MissingInputs {
missing: coverage.missing,
ambiguous: coverage.ambiguous,
}),
Quality::Complete,
)),
MissingPolicy::Fail => Err(MissingPolicyError::Fail {
missing: coverage.missing,
ambiguous: coverage.ambiguous,
}),
}
}