contain 0.5.1

A crate for defining/extending lifetimes
Documentation
use std::hash::Hash;

use indexmap::IndexMap;
use parking_lot::Mutex;

/**
A deduplicating [`Container`] backed by a [`indexmap::IndexMap`].
If two equal items are stored, the second is dropped
and a reference to the first is returned.
Whilst more resource-intensive than [`SimpleContainer`], it can be more memory efficient in scenarios where many
items are equal and equivalent since the duplicates will be dropped.

# Example
```
use contain::DeduplicatingContainer;

fn append_thing<'a>(container: &'a DeduplicatingContainer<'_, String>, s: &str) -> &'a str {
    container.put(format!("{}thing", s))
}

let container = DeduplicatingContainer::new();
let a = append_thing(&container, "some");
let b = append_thing(&container, "a ");
let c = append_thing(&container, "some");
assert_eq!(a, "something");
assert_eq!(b, "a thing");
assert_eq!(c, "something");
assert_eq!(container.count(), 2);
```
*/
pub struct DeduplicatingContainer<'a, T: Eq + Hash + ?Sized> {
    items: Mutex<IndexMap<&'a T, *mut T>>,
}

impl<'a, T: Eq + Hash + ?Sized> DeduplicatingContainer<'a, T> {
    #[must_use]
    pub fn new() -> Self {
        DeduplicatingContainer {
            items: Mutex::new(IndexMap::new()),
        }
    }

    #[must_use]
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            items: Mutex::new(IndexMap::with_capacity(capacity)),
        }
    }

    pub fn count(&self) -> usize {
        self.items.lock().len()
    }
}

impl<'a, T: Eq + Hash> DeduplicatingContainer<'a, T> {
    /**
    Manage the lifetime of a given object.

    # Panics
    May panic if invariants of Eq and Hash are not upheld.
    */
    pub fn put(&self, item: T) -> &T {
        unsafe {
            let mut items = self.items.lock();
            if let Some(existing) = items.get(&item) {
                &**existing
            } else {
                let item_ref = Box::into_raw(Box::new(item));
                items.insert(&*item_ref, item_ref);
                &*item_ref
            }
        }
    }
}

impl<'a, T: Eq + Hash + ?Sized> DeduplicatingContainer<'a, T> {
    pub fn put_boxed(&self, item: Box<T>) -> &T {
        unsafe {
            let mut items = self.items.lock();
            if let Some(existing) = items.get(&*item) {
                &**existing
            } else {
                let item_ref = Box::into_raw(item);
                items.insert(&*item_ref, item_ref);
                &*item_ref
            }
        }
    }
}

impl<'a, T: Eq + Hash + ?Sized> Default for DeduplicatingContainer<'a, T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, T: Eq + Hash + ?Sized> Drop for DeduplicatingContainer<'a, T> {
    fn drop(&mut self) {
        for &p in self.items.lock().values().rev() {
            unsafe {
                let _ = Box::from_raw(p);
            }
        }
    }
}

unsafe impl<'a, T: Eq + Hash + Send + ?Sized> Send for DeduplicatingContainer<'a, T> {}

unsafe impl<'a, T: Eq + Hash + Send + Sync + ?Sized> Sync for DeduplicatingContainer<'a, T> {}