ldp 0.1.0

A library to assist with the creation and maintenance of remote RDF data via LDP
use oxigraph::model::{Quad, QuadRef};
use slotmap::{SecondaryMap, SlotMap, new_key_type};

new_key_type! {
    /// A type which uniquely identifies a quad.
    pub struct QuadKey;
}

/// A set of quads stored in a [`SlotMap`].
///
/// This exists as a convenience for those who want to uniquely identify a quad and associate
/// application-specific data with it.
#[derive(Clone, Debug, Default)]
pub struct KeyedDataset<T> {
    /// The underlying SlotMap.
    pub quads: SlotMap<QuadKey, Quad>,

    /// Application-specific information to be associated with the quads.
    pub associated_data: SecondaryMap<QuadKey, T>,
}

impl<T> FromIterator<Quad> for KeyedDataset<T> {
    fn from_iter<U: IntoIterator<Item = Quad>>(iter: U) -> Self {
        let mut quads = SlotMap::with_key();
        for quad in iter {
            quads.insert(quad);
        }
        Self {
            quads,
            associated_data: SecondaryMap::new(),
        }
    }
}

impl<'a, T> IntoIterator for &'a KeyedDataset<T> {
    type Item = QuadRef<'a>;
    type IntoIter = std::iter::Map<slotmap::basic::Values<'a, QuadKey, Quad>, fn(&Quad) -> QuadRef>;

    fn into_iter(self) -> Self::IntoIter {
        self.quads.values().map(|quad| quad.as_ref())
    }
}

impl<T> KeyedDataset<T> {
    /// Extend the dataset with the provided quads, returning a QuadKey for each insertion.
    pub fn extend(&mut self, quads: impl Iterator<Item = Quad>) -> impl Iterator<Item = QuadKey> {
        quads.map(|quad| self.quads.insert(quad))
    }

    /// Remove a quad and its associated value, if present.
    pub fn remove(&mut self, key: QuadKey) {
        self.quads.remove(key);
        self.associated_data.remove(key);
    }

    /// Remove all quads and all associated data.
    pub fn clear(&mut self) {
        self.quads.clear();
        self.associated_data.clear();
    }

    /// Iterate over both the quads and the associated data. Quads with no corresponding associated
    /// data are filtered out.
    pub fn iter_both(&self) -> impl Iterator<Item = (QuadKey, &Quad, &T)> {
        self.quads
            .iter()
            .filter_map(|(key, quad)| self.associated_data.get(key).map(|ad| (key, quad, ad)))
    }
}