#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResolveError {
None,
Many,
}
pub fn resolve<V>(candidates: impl IntoIterator<Item = Option<V>>) -> Result<V, ResolveError> {
let mut found: Option<V> = None;
for candidate in candidates {
if candidate.is_some() {
if found.is_some() {
return Err(ResolveError::Many);
}
found = candidate;
}
}
found.ok_or(ResolveError::None)
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum V {
A,
B,
C,
}
#[test]
fn empty_candidate_list_is_none() {
let r: Result<V, _> = resolve(std::iter::empty());
assert_eq!(r.unwrap_err(), ResolveError::None);
}
#[test]
fn all_none_is_none() {
let r: Result<V, _> = resolve([None, None, None]);
assert_eq!(r.unwrap_err(), ResolveError::None);
}
#[test]
fn single_some_is_resolved_regardless_of_position() {
assert_eq!(resolve([Some(V::A), None, None]).unwrap(), V::A);
assert_eq!(resolve([None, Some(V::B), None]).unwrap(), V::B);
assert_eq!(resolve([None, None, Some(V::C)]).unwrap(), V::C);
}
#[test]
fn two_or_more_some_is_many() {
assert_eq!(
resolve([Some(V::A), Some(V::B), None]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([Some(V::A), None, Some(V::C)]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([None, Some(V::B), Some(V::C)]).unwrap_err(),
ResolveError::Many
);
assert_eq!(
resolve([Some(V::A), Some(V::B), Some(V::C)]).unwrap_err(),
ResolveError::Many
);
}
#[test]
fn many_short_circuits_after_second_some() {
let mut visited = 0usize;
let candidates = (0..4).map(|i| {
visited += 1;
Some(i)
});
let _ = resolve(candidates);
assert_eq!(visited, 2);
}
#[test]
fn works_with_borrowed_enum_view() {
#[derive(Debug, PartialEq)]
enum View<'a> {
X(&'a u32),
Y(&'a String),
}
let x = 7u32;
let r = resolve([Some(View::X(&x)), None]).unwrap();
assert_eq!(r, View::X(&7));
}
}