liteboxfs 0.2.0

A modern POSIX filesystem in a SQLite database
Documentation
use std::{collections::HashMap, iter::FusedIterator};

/// Key-value pairs associated with a filesystem or root.
///
/// Filesystems and roots can each have arbitrary key-value pairs associated with them. This
/// metadata is stored persistently in the database and is not touched by LiteboxFS, so
/// applications or downstream libraries can use it for any purpose.
///
/// The maximum length of metadata keys and values are bounded by [`MAX_KEY_LEN`] and
/// [`MAX_VALUE_LEN`] respectively.
///
/// You can get and set the metadata for a filesystem using [`Filesystem::filesystem_metadata`] and
/// [`Filesystem::set_filesystem_metadata`]. You can get and set the metadata for a root using
/// [`Filesystem::root_metadata`] and [`Filesystem::set_root_metadata`].
///
/// [`MAX_KEY_LEN`]: UserMetadata::MAX_KEY_LEN
/// [`MAX_VALUE_LEN`]: UserMetadata::MAX_VALUE_LEN
/// [`Filesystem::filesystem_metadata`]: crate::Filesystem::filesystem_metadata
/// [`Filesystem::set_filesystem_metadata`]: crate::Filesystem::set_filesystem_metadata
/// [`Filesystem::root_metadata`]: crate::Filesystem::root_metadata
/// [`Filesystem::set_root_metadata`]: crate::Filesystem::set_root_metadata
#[derive(Debug, Default, Clone)]
pub struct UserMetadata {
    metadata: HashMap<String, Vec<u8>>,
}

// We set a maximum length for metadata keys and values to discourage users from misusing the
// feature to stash large amounts of data—not because of any technical limitation. This API reads
// all the metadata for a filesystem or root into memory at once, so very large metadata entries
// could lead to high memory usage.

impl UserMetadata {
    /// The maximum length of a metadata key, in bytes.
    ///
    /// This value is 256 bytes.
    pub const MAX_KEY_LEN: usize = 2usize.pow(8);

    /// The maximum length of a metadata value, in bytes.
    ///
    /// This value is 16 KiB.
    pub const MAX_VALUE_LEN: usize = 2usize.pow(16);
}

impl UserMetadata {
    /// Create a new empty set of metadata.
    pub fn new() -> Self {
        Self {
            metadata: HashMap::new(),
        }
    }

    /// Get a metadata value by its `key`.
    pub fn get<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<&[u8]> {
        self.metadata.get(name.as_ref()).map(|v| v.as_ref())
    }

    /// Set a metadata value.
    ///
    /// # Errors
    ///
    /// - [`MetadataLimitExceeded`]: The given `key` or `value` exceeds the maximum allowed length.
    ///
    /// [`MetadataLimitExceeded`]: crate::Error::MetadataLimitExceeded
    pub fn set(&mut self, key: String, value: Vec<u8>) -> crate::Result<()> {
        if key.len() > Self::MAX_KEY_LEN || value.len() > Self::MAX_VALUE_LEN {
            return Err(crate::Error::MetadataLimitExceeded);
        }

        self.metadata.insert(key, value);

        Ok(())
    }

    /// Remove a metadata value.
    pub fn remove<S: AsRef<str> + ?Sized>(&mut self, name: &S) {
        self.metadata.remove(name.as_ref());
    }

    /// Clear all metadata.
    pub fn clear(&mut self) {
        self.metadata.clear();
    }

    /// An iterator over the metadata key-value pairs.
    pub fn iter(&self) -> UserMetadataIter<'_> {
        UserMetadataIter {
            inner: self.metadata.iter(),
        }
    }
}

/// A borrowing iterator over a [`UserMetadata`].
#[derive(Debug)]
pub struct UserMetadataIter<'a> {
    inner: std::collections::hash_map::Iter<'a, String, Vec<u8>>,
}

impl<'a> Iterator for UserMetadataIter<'a> {
    type Item = (&'a str, &'a [u8]);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(k, v)| (k.as_ref(), v.as_ref()))
    }
}

impl<'a> FusedIterator for UserMetadataIter<'a> {}

impl<'a> ExactSizeIterator for UserMetadataIter<'a> {
    fn len(&self) -> usize {
        self.inner.len()
    }
}

/// An owned iterator over a [`UserMetadata`].
#[derive(Debug)]
pub struct UserMetadataIntoIter {
    inner: std::collections::hash_map::IntoIter<String, Vec<u8>>,
}

impl Iterator for UserMetadataIntoIter {
    type Item = (String, Vec<u8>);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }
}

impl FusedIterator for UserMetadataIntoIter {}

impl ExactSizeIterator for UserMetadataIntoIter {
    fn len(&self) -> usize {
        self.inner.len()
    }
}

impl IntoIterator for UserMetadata {
    type Item = (String, Vec<u8>);
    type IntoIter = UserMetadataIntoIter;

    fn into_iter(self) -> Self::IntoIter {
        UserMetadataIntoIter {
            inner: self.metadata.into_iter(),
        }
    }
}

impl<'a> IntoIterator for &'a UserMetadata {
    type Item = (&'a str, &'a [u8]);
    type IntoIter = UserMetadataIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}