microrm 0.6.3

Lightweight ORM using sqlite as a backend
Documentation
use crate::{
    db::{StatementContext, StatementRow},
    schema::entity::{Entity, EntityVisitor},
    schema::relation::{Relation, RelationData, RelationDomain, RelationMap, RelationRange},
    DBResult,
};

// trait implementations for Datum
mod datum_common;
// trait implementations for DatumList
mod datum_list;

// ----------------------------------------------------------------------
// Datum and related types
// ----------------------------------------------------------------------

/// Represents a data field in an Entity.
pub trait Datum: Clone + std::fmt::Debug {
    /// True if the datum is 'internal' and has a dedicated column.
    const INTERNAL_DATUM: bool = true;

    /// The type used in a SQL database to represent this datum.
    fn sql_type() -> &'static str;

    /// Generate a `std::fmt::Debug`-compatible view of the current datum; used to produce more
    /// human-friendly outputs for some more complicated types with state stored in the database.
    fn debug_field(&self, field: &'static str, fmt: &mut std::fmt::DebugStruct)
    where
        Self: Sized,
    {
        fmt.field(field, self);
    }

    /// Bind this datum to a [`StatementContext`] at a given index.
    fn bind_to(&self, _stmt: &mut StatementContext, index: i32) -> DBResult<()>;
    /// Construct an instance of this datum from a table row.
    fn build_from(
        _adata: RelationData,
        _stmt: &mut StatementRow,
        _index: &mut i32,
    ) -> DBResult<Self>
    where
        Self: Sized,
    {
        unreachable!()
    }

    /// Update any [`RelationData`] instances.
    #[doc(hidden)]
    fn update_adata(&mut self, _adata: RelationData) {}

    /// Accept an entity visitor to iterate across any entities this Datum type references.
    fn accept_entity_visitor(_: &mut impl EntityVisitor) {}
    /// Accept a datum discriminator without any instance reference to allow for more fine-grained per-type processing.
    fn accept_discriminator(d: &mut impl DatumDiscriminator)
    where
        Self: Sized,
    {
        d.visit_bare_field::<Self>();
    }

    /// Accept a datum discriminator with an instance reference to allow for more fine-grained per-type processing.
    fn accept_discriminator_ref(&self, d: &mut impl DatumDiscriminatorRef)
    where
        Self: Sized,
    {
        d.visit_bare_field::<Self>(self);
    }
}

/// Trait for 'owned' datums, i.e. those with a static lifetime.
pub trait OwnedDatum: 'static + Datum {
    /// 'Canonical' reference type for this owned datum, usually &Self.
    type RefData<'a>: Datum + BorrowedDatum<'a, Self>;
    /// Acquires a borrow of the current datum.
    fn as_ref(&self) -> Self::RefData<'_>;
}

/// Trait for borrowed datums, i.e. that are trivially Copy.
pub trait BorrowedDatum<'a, Own: OwnedDatum>: Copy + Datum {
    /// Converts borrow into an owned datum.
    fn as_owned(&self) -> Own;
}

/// Visitor for allowing for type-specific behaviour.
pub trait DatumDiscriminator {
    /// Visit an `EntityID` type.
    fn visit_entity_id<E: Entity>(&mut self);
    /// Visit a `Serialized<T>` instance.
    fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self);
    /// Visit a bare datum field instance, such as a `String` or `usize`.
    fn visit_bare_field<T: Datum>(&mut self);
    /// Visit a one-direction relation map.
    fn visit_relation_map<E: Entity>(&mut self);
    /// Visit the domain side of a bidirectional relation map.
    fn visit_relation_domain<R: Relation>(&mut self);
    /// Visit the range side of a bidirectional relation map.
    fn visit_relation_range<R: Relation>(&mut self);
    /// Visit a type annotated with the Value derivation macro.
    fn visit_value<T: serde::de::DeserializeOwned>(&mut self);
}

/// Visitor for allowing for type-specific behaviour, with instances passed as references.
pub trait DatumDiscriminatorRef {
    /// Visit an `EntityID` instance.
    fn visit_entity_id<E: Entity>(&mut self, _: &E::ID);
    /// Visit a `Serialized<T>` instance.
    fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self, _: &T);
    /// Visit a bare datum field instance, such as a `String` or `usize`.
    fn visit_bare_field<T: Datum>(&mut self, _: &T);
    /// Visit a one-direction relation map instance.
    fn visit_relation_map<E: Entity>(&mut self, _: &RelationMap<E>);
    /// Visit an instance of the domain side of a bidirectional relation map.
    fn visit_relation_domain<R: Relation>(&mut self, _: &RelationDomain<R>);
    /// Visit an instance of the range side of a bidirectional relation map.
    fn visit_relation_range<R: Relation>(&mut self, _: &RelationRange<R>);
    /// Visit a type annotated with the Value derivation macro.
    fn visit_value<T: serde::de::DeserializeOwned>(&mut self, _: &T);
}

/// A list of owned datums.
pub trait OwnedDatumList: Clone {
    /// Recursion control: true if this is an empty list.
    const IS_EMPTY: bool = false;
    /// The first [`OwnedDatum`] in this list, or a sentinel for an empty list.
    type ListHead: OwnedDatum;
    /// Another `OwnedDatumList` containing all but the first datums in this list.
    type ListTail: OwnedDatumList;

    /// A version of this [`OwnedDatumList`] where each element is replaced by a reference to the
    /// element, turning `&(T1, T2)` into i.e. `(&T1, &T2)`.
    type RefList<'l>: BorrowedDatumList<'l, Self>
    where
        Self: 'l;

    /// Get an instance of the first element of this list.
    fn list_head(&self) -> &Self::ListHead;
    /// Create a copy of the tail of this list.
    fn list_tail(&self) -> Self::ListTail;

    /// Accept a datum visitor for iteration.
    fn accept(&self, visitor: &mut impl DatumVisitor) -> DBResult<()>;

    /// Builds a [`BorrowedDatumList`] instance using [`StringQuery`]. Panics if the length of the
    /// `strs` iterator is less than the length of this list.
    fn build_equivalent<'l>(
        strs: impl Iterator<Item = &'l str>,
    ) -> impl BorrowedDatumList<'l, Self>;
}

/// A list of borrowed datums.
pub trait BorrowedDatumList<'l, O: OwnedDatumList>: Copy {
    /// Recursion control: true if this is an empty list.
    const IS_EMPTY: bool = false;
    /// The first [`Datum`] in this list, or a sentinel for an empty list.
    type ListHead: BorrowedDatum<'l, O::ListHead>;
    /// Another `BorrowedDatumList` containing all but the first datum in this list.
    type ListTail: BorrowedDatumList<'l, O::ListTail>;

    /// Get an instance of the first element of this list.
    fn list_head(&self) -> &Self::ListHead;
    /// Create a copy of the tail of this list.
    fn list_tail(&self) -> Self::ListTail;

    /// Accept a datum visitor for iteration.
    fn accept(&self, visitor: &mut impl DatumVisitor) -> DBResult<()>;
}

/// A walker for DatumList instances.
pub trait DatumVisitor {
    /// Visit a specific datum in a DatumList.
    fn visit<ED: Datum>(&mut self, datum: &ED) -> DBResult<()>;
}

/// Use if you want to leverage sqlite's built-in string/type equivalence support.
#[derive(Debug, Clone, Copy)]
pub struct StringQuery<'l>(pub &'l str);

impl<'l> Datum for StringQuery<'l> {
    fn sql_type() -> &'static str {
        "N/A"
    }

    fn bind_to(&self, stmt: &mut StatementContext, index: i32) -> DBResult<()> {
        stmt.bind(index, self.0)
    }

    fn build_from(_: RelationData, _: &mut StatementRow, _: &mut i32) -> DBResult<Self> {
        unreachable!()
    }
}

impl<'l, O: OwnedDatum> BorrowedDatum<'l, O> for StringQuery<'l> {
    fn as_owned(&self) -> O {
        unreachable!()
    }
}