#![warn(missing_docs)]
#![deny(unsafe_code)]
pub struct Fallback<T> {
data: Option<T>,
base_data: Option<T>,
}
impl<T> Fallback<T> {
pub const fn new(data: Option<T>, base_data: Option<T>) -> Self {
Self { data, base_data }
}
pub const fn is_some(&self) -> bool {
self.data.is_some() || self.base_data.is_some()
}
pub const fn as_ref(&self) -> Fallback<&T> {
Fallback::new(self.data.as_ref(), self.base_data.as_ref())
}
pub fn and_then<V>(self, mut f: impl FnMut(T) -> Option<V>) -> Option<V> {
self.data
.and_then(&mut f)
.or_else(|| self.base_data.and_then(&mut f))
}
pub fn fallback(self) -> Option<T> {
self.data.or(self.base_data)
}
pub fn map<V>(self, mut f: impl FnMut(T) -> V) -> Fallback<V> {
Fallback::new(self.data.map(&mut f), self.base_data.map(&mut f))
}
pub fn unzip(self) -> (Option<T>, Option<T>) {
(self.data, self.base_data)
}
}
impl<T> Fallback<Option<T>> {
pub fn flatten(self) -> Fallback<T> {
Fallback::new(self.data.flatten(), self.base_data.flatten())
}
}
impl<T> Fallback<T>
where
for<'a> &'a T: IntoIterator,
for<'a> <&'a T as IntoIterator>::IntoIter: ExactSizeIterator,
{
pub fn and_any(self) -> Option<T> {
self.and_then(|s| {
if s.into_iter().len() == 0 {
None
} else {
Some(s)
}
})
}
}
impl<T: AsRef<str>> Fallback<T> {
pub fn and_any_str(self) -> Option<T> {
self.and_then(|s| if s.as_ref().is_empty() { None } else { Some(s) })
}
}
impl<T> From<Fallback<T>> for Option<T> {
fn from(f: Fallback<T>) -> Self {
if f.data.is_some() {
f.data
} else {
f.base_data
}
}
}
#[doc(hidden)]
pub struct FallbackIter<A> {
data: Option<A>,
base_data: Option<A>,
}
impl<A: Iterator> Iterator for FallbackIter<A> {
type Item = Fallback<A::Item>;
fn next(&mut self) -> Option<Self::Item> {
let d = self.data.as_mut().and_then(|data| data.next());
let based = self.base_data.as_mut().and_then(|data| data.next());
if d.is_some() || based.is_some() {
Some(Fallback::new(d, based))
} else {
None
}
}
}
impl<T: IntoIterator> IntoIterator for Fallback<T> {
type Item = Fallback<T::Item>;
type IntoIter = FallbackIter<std::iter::Fuse<T::IntoIter>>;
fn into_iter(self) -> Self::IntoIter {
FallbackIter {
data: self.data.map(|data| data.into_iter().fuse()),
base_data: self.base_data.map(|data| data.into_iter().fuse()),
}
}
}
pub trait FallbackSpec: Sized {
type SpecType: From<Fallback<Self>>;
}
impl<T: FallbackSpec> Fallback<T> {
pub fn spec(self) -> T::SpecType {
T::SpecType::from(self)
}
}
pub use fallback_derive::FallbackSpec;
#[cfg(test)]
mod test {
use crate::*;
#[test]
fn some() {
assert!(!Fallback::<()>::new(None, None).is_some());
}
#[test]
fn option() {
let f = Fallback::new(None, Some(100));
assert_eq!(Option::from(f), Some(100));
}
#[test]
fn empty() {
let f = Fallback::new(Some(vec![]), Some(vec![1, 1, 4, 5, 1, 4]));
assert_eq!(f.and_any(), Some(vec![1, 1, 4, 5, 1, 4]));
let f = Fallback::new(Some(String::new()), Some("Hello world!".to_string()));
assert_eq!(f.and_any_str(), Some("Hello world!".to_string()));
}
#[test]
fn iter() {
let f = Fallback::new(Some(vec![3, 2, 1]), Some(vec![1, 1, 4, 5, 1, 4]));
assert_eq!(
f.into_iter()
.map(|data| data.fallback().unwrap())
.collect::<Vec<_>>(),
[3, 2, 1, 5, 1, 4]
);
}
}