bonsaidb-core 0.5.0

Core functionality and types for BonsaiDb.
Documentation
use std::fmt::{Display, Write};

use serde::{Deserialize, Serialize};

use crate::document::{BorrowedDocument, CollectionDocument, DocumentId, OwnedDocument, Revision};
use crate::key::Key;
use crate::schema::view::map::Mappings;
use crate::schema::{Map, SerializedCollection};

/// The header of a `Document`.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
pub struct Header {
    /// The id of the Document. Unique across the collection the document is
    /// contained within.
    pub id: DocumentId,

    /// The revision of the stored document.
    pub revision: Revision,
}

/// A type that can return a [`Header`].
pub trait HasHeader {
    /// Returns the header for this instance.
    fn header(&self) -> Result<Header, crate::Error>;
}

impl HasHeader for Header {
    fn header(&self) -> Result<Header, crate::Error> {
        Ok(self.clone())
    }
}

/// View mapping emit functions. Used when implementing a view's `map()`
/// function.
pub trait Emit {
    /// Creates a `Map` result with an empty key and value.
    fn emit(&self) -> Result<Mappings<(), ()>, crate::Error> {
        self.emit_key_and_value((), ())
    }

    /// Creates a `Map` result with an empty key and value if `condition` is
    /// true.
    fn emit_if(&self, condition: bool) -> Result<Mappings<(), ()>, crate::Error> {
        if condition {
            self.emit()
        } else {
            Ok(Mappings::default())
        }
    }

    /// Creates a `Map` result with a `key` and an empty value.
    fn emit_key<K>(&self, key: K) -> Result<Mappings<K, ()>, crate::Error> {
        self.emit_key_and_value(key, ())
    }

    /// Creates a `Map` result with `value` and an empty key.
    fn emit_value<Value>(&self, value: Value) -> Result<Mappings<(), Value>, crate::Error> {
        self.emit_key_and_value((), value)
    }

    /// Creates a `Map` result with a `key` and `value`.
    fn emit_key_and_value<K, Value>(
        &self,
        key: K,
        value: Value,
    ) -> Result<Mappings<K, Value>, crate::Error>;
}

impl Emit for Header {
    fn emit_key_and_value<K, Value>(
        &self,
        key: K,
        value: Value,
    ) -> Result<Mappings<K, Value>, crate::Error> {
        Ok(Mappings::Simple(Some(Map::new(self.clone(), key, value))))
    }
}

impl Display for Header {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.id.fmt(f)?;
        f.write_char('@')?;
        self.revision.fmt(f)
    }
}

/// A header for a [`CollectionDocument`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub struct CollectionHeader<PrimaryKey> {
    /// The unique id of the document.
    pub id: PrimaryKey,
    /// The revision of the document.
    pub revision: Revision,
}

impl<PrimaryKey> Emit for CollectionHeader<PrimaryKey>
where
    PrimaryKey: for<'k> Key<'k>,
{
    fn emit_key_and_value<K, Value>(
        &self,
        key: K,
        value: Value,
    ) -> Result<Mappings<K, Value>, crate::Error> {
        let header = Header::try_from(self.clone())?;
        Ok(Mappings::Simple(Some(Map::new(header, key, value))))
    }
}

impl<PrimaryKey> HasHeader for CollectionHeader<PrimaryKey>
where
    PrimaryKey: for<'k> Key<'k>,
{
    fn header(&self) -> Result<Header, crate::Error> {
        Header::try_from(self.clone())
    }
}

impl HasHeader for OwnedDocument {
    fn header(&self) -> Result<Header, crate::Error> {
        self.header.header()
    }
}

impl<'a> HasHeader for BorrowedDocument<'a> {
    fn header(&self) -> Result<Header, crate::Error> {
        self.header.header()
    }
}

impl<C> HasHeader for CollectionDocument<C>
where
    C: SerializedCollection,
{
    fn header(&self) -> Result<Header, crate::Error> {
        self.header.header()
    }
}

impl<PrimaryKey> TryFrom<Header> for CollectionHeader<PrimaryKey>
where
    PrimaryKey: for<'k> Key<'k>,
{
    type Error = crate::Error;

    fn try_from(value: Header) -> Result<Self, Self::Error> {
        Ok(Self {
            id: value.id.deserialize::<PrimaryKey>()?,
            revision: value.revision,
        })
    }
}

impl<'a, PrimaryKey> TryFrom<&'a Header> for CollectionHeader<PrimaryKey>
where
    PrimaryKey: for<'k> Key<'k>,
{
    type Error = crate::Error;

    fn try_from(value: &'a Header) -> Result<Self, Self::Error> {
        Ok(Self {
            id: value.id.deserialize::<PrimaryKey>()?,
            revision: value.revision,
        })
    }
}

impl<PrimaryKey> TryFrom<CollectionHeader<PrimaryKey>> for Header
where
    PrimaryKey: for<'k> Key<'k>,
{
    type Error = crate::Error;

    fn try_from(value: CollectionHeader<PrimaryKey>) -> Result<Self, Self::Error> {
        Ok(Self {
            id: DocumentId::new(&value.id)?,
            revision: value.revision,
        })
    }
}

impl<'a, PrimaryKey> TryFrom<&'a CollectionHeader<PrimaryKey>> for Header
where
    PrimaryKey: for<'k> Key<'k>,
{
    type Error = crate::Error;

    fn try_from(value: &'a CollectionHeader<PrimaryKey>) -> Result<Self, Self::Error> {
        Ok(Self {
            id: DocumentId::new(&value.id)?,
            revision: value.revision,
        })
    }
}

/// A header with either a serialized or deserialized primary key.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AnyHeader<PrimaryKey> {
    /// A serialized header.
    Serialized(Header),
    /// A deserialized header.
    Collection(CollectionHeader<PrimaryKey>),
}

impl<PrimaryKey> AnyHeader<PrimaryKey>
where
    PrimaryKey: for<'k> Key<'k>,
{
    /// Returns the contained header as a [`Header`].
    pub fn into_header(self) -> Result<Header, crate::Error> {
        match self {
            AnyHeader::Serialized(header) => Ok(header),
            AnyHeader::Collection(header) => Header::try_from(header),
        }
    }
}

#[test]
fn emissions_tests() -> Result<(), crate::Error> {
    use crate::schema::Map;
    use crate::test_util::Basic;

    let doc = BorrowedDocument::with_contents::<Basic, _>(&1, &Basic::default())?;

    assert_eq!(
        doc.header.emit()?,
        Mappings::Simple(Some(Map::new(doc.header.clone(), (), ())))
    );

    assert_eq!(
        doc.header.emit_key(1)?,
        Mappings::Simple(Some(Map::new(doc.header.clone(), 1, ())))
    );

    assert_eq!(
        doc.header.emit_value(1)?,
        Mappings::Simple(Some(Map::new(doc.header.clone(), (), 1)))
    );

    assert_eq!(
        doc.header.emit_key_and_value(1, 2)?,
        Mappings::Simple(Some(Map::new(doc.header, 1, 2)))
    );

    Ok(())
}

#[test]
fn chained_mappings_test() -> Result<(), crate::Error> {
    use crate::schema::Map;
    use crate::test_util::Basic;

    let doc = BorrowedDocument::with_contents::<Basic, _>(&1, &Basic::default())?;

    assert_eq!(
        doc.header.emit()?.and(doc.header.emit()?),
        Mappings::List(vec![
            Map::new(doc.header.clone(), (), ()),
            Map::new(doc.header, (), ())
        ])
    );

    Ok(())
}

#[test]
fn header_display_test() {
    let original_contents = b"one";
    let revision = Revision::new(original_contents);
    let header = Header {
        id: DocumentId::new(&42_u64).unwrap(),
        revision,
    };
    assert_eq!(
        header.to_string(),
        "7$2a@0-7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed"
    );
}