#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Dup<T>(Vec<T>);
impl<T> Default for Dup<T> {
fn default() -> Self {
Self(Vec::new())
}
}
impl<T> Dup<T> {
pub fn get(&self) -> Option<&T> {
self.0.last()
}
pub fn get_mut(&mut self) -> Option<&mut T> {
self.0.last_mut()
}
pub fn into_value(self) -> Option<T> {
self.0.into_iter().next_back()
}
pub fn cloned(&self) -> Option<T>
where
T: Clone,
{
self.0.last().cloned()
}
pub fn all(&self) -> &[T] {
&self.0
}
pub fn is_absent(&self) -> bool {
self.0.is_empty()
}
pub fn is_duplicated(&self) -> bool {
self.0.len() > 1
}
pub fn map<U>(self, f: impl FnMut(T) -> U) -> Dup<U> {
Dup(self.0.into_iter().map(f).collect())
}
pub fn filter_map<U>(self, f: impl FnMut(T) -> Option<U>) -> Dup<U> {
Dup(self.0.into_iter().filter_map(f).collect())
}
pub fn map_ref<U>(&self, f: impl FnMut(&T) -> U) -> Dup<U> {
Dup(self.0.iter().map(f).collect())
}
}
impl<T> From<Vec<T>> for Dup<T> {
fn from(v: Vec<T>) -> Self {
Self(v)
}
}
impl<T> From<Option<T>> for Dup<T> {
fn from(v: Option<T>) -> Self {
Self(v.into_iter().collect())
}
}
impl<T> FromIterator<T> for Dup<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self(iter.into_iter().collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn absent_reads_as_none() {
let d: Dup<u8> = Dup::default();
assert!(d.is_absent());
assert_eq!(d.get(), None);
assert_eq!(d.clone().into_value(), None);
assert!(!d.is_duplicated());
}
#[test]
fn a_single_occurrence_is_itself() {
let d = Dup::from(vec![7]);
assert_eq!(d.get(), Some(&7));
assert!(!d.is_duplicated());
}
#[test]
fn the_last_occurrence_wins() {
let d = Dup::from(vec![1, 2, 3]);
assert_eq!(d.get(), Some(&3));
assert_eq!(d.clone().into_value(), Some(3));
assert_eq!(Dup::from(vec![3, 2, 1]).get(), Some(&1));
}
#[test]
fn the_losing_occurrences_survive_into_the_model() {
let d = Dup::from(vec![1, 2, 3]);
assert!(d.is_duplicated());
assert_eq!(d.all(), &[1, 2, 3]);
assert_eq!(d.all().first(), Some(&1), "first-wins is still reachable");
}
#[test]
fn map_preserves_every_occurrence() {
let d = Dup::from(vec![1, 2, 3]).map(|n| n * 10);
assert_eq!(d.all(), &[10, 20, 30], "map is a functor, not a resolution");
assert_eq!(
d.get(),
Some(&30),
"and the policy still applies at the read"
);
}
#[test]
fn filter_map_drops_only_what_it_is_told_to() {
let d = Dup::from(vec![1, 2, 3]).filter_map(|n| (n % 2 == 1).then_some(n));
assert_eq!(d.all(), &[1, 3]);
}
#[test]
fn mapping_an_absent_property_stays_absent() {
assert!(Dup::<u8>::default().map(|n| n + 1).is_absent());
}
#[test]
fn it_round_trips_an_option() {
assert_eq!(Dup::from(Some(5)).get(), Some(&5));
assert_eq!(Dup::from(None::<u8>).get(), None);
}
}