1use super::analysis::{Coverage, Status};
10use super::policy::MissingPolicy;
11use std::fmt;
12
13#[derive(Clone, PartialEq, Eq, Debug)]
15#[non_exhaustive]
16pub enum Indeterminacy {
17 MissingInputs {
19 missing: u32,
21 ambiguous: u32,
23 },
24 UnresolvedConformations,
26 Frame {
28 frame: usize,
30 reason: Box<Indeterminacy>,
32 },
33 Other(Box<str>),
35}
36
37impl fmt::Display for Indeterminacy {
38 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 Self::MissingInputs { missing, ambiguous } => write!(
41 formatter,
42 "the policy rejects {missing} missing and {ambiguous} ambiguous inputs"
43 ),
44 Self::UnresolvedConformations => {
45 formatter.write_str("the alternate-conformation rule selected no consistent atoms")
46 }
47 Self::Frame { frame, reason } => write!(formatter, "frame {frame}: {reason}"),
48 Self::Other(reason) => formatter.write_str(reason),
49 }
50 }
51}
52
53impl std::error::Error for Indeterminacy {}
54
55#[derive(Clone, PartialEq, Eq, Debug)]
61pub enum Outcome<T> {
62 Determinate(T),
64 Indeterminate(Indeterminacy),
66}
67
68impl<T> Outcome<T> {
69 #[must_use]
71 pub const fn is_determinate(&self) -> bool {
72 matches!(self, Self::Determinate(_))
73 }
74
75 #[must_use]
77 pub const fn value(&self) -> Option<&T> {
78 match self {
79 Self::Determinate(value) => Some(value),
80 Self::Indeterminate(_) => None,
81 }
82 }
83
84 #[must_use]
86 pub const fn indeterminacy(&self) -> Option<&Indeterminacy> {
87 match self {
88 Self::Determinate(_) => None,
89 Self::Indeterminate(reason) => Some(reason),
90 }
91 }
92
93 pub fn into_result(self) -> Result<T, Indeterminacy> {
99 match self {
100 Self::Determinate(value) => Ok(value),
101 Self::Indeterminate(reason) => Err(reason),
102 }
103 }
104
105 pub const fn as_result(&self) -> Result<&T, &Indeterminacy> {
111 match self {
112 Self::Determinate(value) => Ok(value),
113 Self::Indeterminate(reason) => Err(reason),
114 }
115 }
116
117 pub fn map<U>(self, transform: impl FnOnce(T) -> U) -> Outcome<U> {
119 match self {
120 Self::Determinate(value) => Outcome::Determinate(transform(value)),
121 Self::Indeterminate(reason) => Outcome::Indeterminate(reason),
122 }
123 }
124}
125
126#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
131pub enum Quality {
132 #[default]
134 Complete,
135 Partial,
137 Ambiguous,
139}
140
141impl Quality {
142 #[must_use]
144 pub fn worst(self, other: Self) -> Self {
145 match (self, other) {
147 (Self::Ambiguous, _) | (_, Self::Ambiguous) => Self::Ambiguous,
148 (Self::Partial, _) | (_, Self::Partial) => Self::Partial,
149 (Self::Complete, Self::Complete) => Self::Complete,
150 }
151 }
152}
153
154impl From<Quality> for Status {
155 fn from(quality: Quality) -> Self {
156 match quality {
157 Quality::Complete => Self::Complete,
158 Quality::Partial => Self::Partial,
159 Quality::Ambiguous => Self::Ambiguous,
160 }
161 }
162}
163
164#[derive(Clone, Copy, PartialEq, Eq, Debug)]
166#[non_exhaustive]
167pub enum MissingPolicyError {
168 Fail {
170 missing: u32,
172 ambiguous: u32,
174 },
175}
176
177impl fmt::Display for MissingPolicyError {
178 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
179 match self {
180 Self::Fail { missing, ambiguous } => write!(
181 formatter,
182 "the policy fails on {missing} missing and {ambiguous} ambiguous inputs"
183 ),
184 }
185 }
186}
187
188impl std::error::Error for MissingPolicyError {}
189
190pub fn resolve_missing<T>(
203 value: T,
204 quality: Quality,
205 coverage: Coverage,
206 policy: MissingPolicy,
207) -> Result<(Outcome<T>, Quality), MissingPolicyError> {
208 if coverage.missing == 0 && coverage.ambiguous == 0 {
209 return Ok((Outcome::Determinate(value), quality));
210 }
211 match policy {
212 MissingPolicy::Ignore => Ok((Outcome::Determinate(value), quality)),
213 MissingPolicy::Report => Ok((Outcome::Determinate(value), quality.worst(Quality::Partial))),
214 MissingPolicy::Indeterminate => Ok((
215 Outcome::Indeterminate(Indeterminacy::MissingInputs {
216 missing: coverage.missing,
217 ambiguous: coverage.ambiguous,
218 }),
219 Quality::Complete,
220 )),
221 MissingPolicy::Fail => Err(MissingPolicyError::Fail {
222 missing: coverage.missing,
223 ambiguous: coverage.ambiguous,
224 }),
225 }
226}