microrm 0.6.3

Lightweight ORM using sqlite as a backend
Documentation
use std::{fmt::Debug, hash::Hash};

use crate::{db::StatementRow, schema::datum::Datum, DBResult};

use super::datum::{BorrowedDatum, OwnedDatum, OwnedDatumList};

/// Integral identifier for an entity.
pub trait EntityID:
    'static
    + PartialEq
    + Hash
    + PartialOrd
    + Debug
    + Copy
    + OwnedDatum<RefData<'static> = Self>
    + BorrowedDatum<'static, Self>
{
    /// The entity that this ID is representing.
    type Entity: Entity<ID = Self>;

    /// Construct from a raw integer.
    ///
    /// Do not use this unless you are _very_ certain it is what you want.
    fn from_raw(id: i64) -> Self;
    /// Produces a raw integer from an ID. Of dubious use.
    fn into_raw(self) -> i64;
}

// ----------------------------------------------------------------------
// EntityPart and related types
// ----------------------------------------------------------------------

/// A single data field in an Entity, automatically declared and derived as part of `#[derive(Entity)]`.
pub trait EntityPart: Default + Clone + 'static {
    /// The type of the data field.
    type Datum: OwnedDatum;
    /// The type of the struct this field is a member of.
    type Entity: Entity;

    /// String representing the field name.
    fn part_name() -> &'static str;
    /// Whether this field has a `#[unique]` attribute or not.
    fn unique() -> bool;
    /// Documentation comment for this field.
    fn desc() -> Option<&'static str>;

    /// Accessor function: given an instance of the outer struct, return this field's value in that instance.
    fn get_datum(from: &Self::Entity) -> &Self::Datum;
}

/// Visitor for traversing all [`EntityPart`]s in an [`Entity`] or [`EntityPartList`].
pub trait EntityPartVisitor {
    /// The [`Entity`] that this visitor will traverse over the parts of.
    type Entity: Entity;
    /// Visit a part, with no instance information.
    fn visit<EP: EntityPart<Entity = Self::Entity>>(&mut self) {}
    /// Visit a part, with a datum reference.
    fn visit_datum<EP: EntityPart<Entity = Self::Entity>>(
        &mut self,
        _datum: &EP::Datum,
    ) -> DBResult<()> {
        Ok(())
    }
    /// Visit a part, with a mutable datum reference.
    fn visit_datum_mut<EP: EntityPart<Entity = Self::Entity>>(&mut self, _datum: &mut EP::Datum) {}
}

/// List of EntityParts.
pub trait EntityPartList: 'static {
    /// The [`Entity`] that this list represents the parts of.
    type Entity: Entity;
    /// An [`OwnedDatumList`] type that matches this part list, formed as the `EntityPart::Datum`s of
    /// each `EntityPart` in this list.
    type DatumList: OwnedDatumList;
    // + DatumList<ListTail = <Self::ListTail as EntityPartList>::DatumList>;

    /// The first element of this list. If this list is empty, this will be a generic marker type
    /// with poisoned values to be easily visible if processed.
    type ListHead: EntityPart<
        Entity = Self::Entity,
        Datum = <Self::DatumList as OwnedDatumList>::ListHead,
    >;
    /// An [`EntityPartList`] that represents all but the first element of this list.
    type ListTail: EntityPartList<
        Entity = Self::Entity,
        DatumList = <Self::DatumList as OwnedDatumList>::ListTail,
    >;
    /// Whether the current list is empty. Should be used to control recursion if iterating across
    /// the list.
    const IS_EMPTY: bool = false;

    /// Construct an instance of `Self::DatumList` from a table row.
    fn build_datum_list(stmt: &mut StatementRow) -> DBResult<Self::DatumList>;

    /// Construct an instance of `Self::DatumList::RefList` from a table row.
    fn build_datum_ref_list<'l, 'm>(
        stmt: &'l mut StatementRow,
    ) -> DBResult<<Self::DatumList as OwnedDatumList>::RefList<'m>>
    where
        'l: 'm;

    /// Accept a visitor to iterate across each part in the list, with no datum instances.
    fn accept_part_visitor(_: &mut impl EntityPartVisitor<Entity = Self::Entity>);
    /// Accept a visitor to iterate across each part in the list, with datum instances provided by
    /// a `Self::DatumList` instance.
    fn accept_part_visitor_ref(
        datum_list: &Self::DatumList,
        _: &mut impl EntityPartVisitor<Entity = Self::Entity>,
    ) -> DBResult<()>;
}

// trait implementations for EntityPartList
mod part_list;

/// A sentinel type representing a zero-length [`EntityPartList`].
pub use part_list::EmptyList;

// ----------------------------------------------------------------------
// Entity and related types
// ----------------------------------------------------------------------

/// Helper for accessing the canonical [`BorrowedDatumList`](super::datum::BorrowedDatumList) type that references an entity's data.
pub type EntityRefDatumList<'l, E> =
    <<<E as Entity>::Parts as EntityPartList>::DatumList as OwnedDatumList>::RefList<'l>;

/// A reference 'shell' of an [`Entity`].
pub trait EntityRef<'a>: Clone + Copy {
    /// The entity this type represents the data of.
    type Entity: Entity;

    /// Convert this 'shell' into an owned type.
    fn as_entity(&self) -> Self::Entity;

    /// Convert this reference type to a [`BorrowedDatumList`](super::datum::BorrowedDatumList) for type-blind data access.
    fn as_borrowed_list<'r>(&self) -> EntityRefDatumList<'r, Self::Entity>
    where
        'a: 'r;

    /// Build an entity reference type from a list of borrowed datums.
    fn from_borrowed_list<'r>(l: EntityRefDatumList<'r, Self::Entity>) -> Self
    where
        'r: 'a;
}

/// A single database entity, aka an object type that gets its own table.
pub trait Entity: 'static + std::fmt::Debug {
    /// A list of all the [`EntityPart`]s that make up the fields of the struct.
    type Parts: EntityPartList<Entity = Self>;
    /// A list of the [`EntityPart`]s that make up the list of access keys for the entity. Can be an
    /// [`EmptyList`] if no keys are defined.
    type Keys: EntityPartList<Entity = Self>;
    /// The corresponding [`EntityID`] type for this Entity.
    type ID: EntityID<Entity = Self>;
    /// The [`EntityPart`] type for `Self::ID`.
    type IDPart: EntityPart<Datum = Self::ID, Entity = Self>;
    /// A reference version of the current entity, can be cheaply copied.
    type ERef<'a>: EntityRef<'a, Entity = Self>;

    /// Create a reference 'shell' of the current entity.
    fn as_ref(&self) -> Self::ERef<'_>
    where
        Self: Sized;

    /// Construct an instance of this entity from a list of datums that matches each part, in
    /// order.
    fn build(values: <Self::Parts as EntityPartList>::DatumList) -> Self;

    /// A static string used as the database table name for the entity.
    fn entity_name() -> &'static str;
    /// Accept a visitor to iterate across each part in the entity, with no datum instances.
    fn accept_part_visitor(visitor: &mut impl EntityPartVisitor<Entity = Self>);
    /// Accept a visitor to iterate across each part in the entity, with datum instances pulled
    /// from an instance of `Self`.
    fn accept_part_visitor_ref(
        &self,
        visitor: &mut impl EntityPartVisitor<Entity = Self>,
    ) -> DBResult<()>;
    /// Accept a visitor to iterate across each part in the entity, with mutable datum instances
    /// pulled from an instance of `Self`.
    fn accept_part_visitor_mut(&mut self, _: &mut impl EntityPartVisitor<Entity = Self>);
}

/// Visitor for traversing all [`Entity`]s in a container type.
pub trait EntityVisitor {
    /// Visit a particular entity.
    fn visit<E: Entity>(&mut self);
}

// ----------------------------------------------------------------------
// EntityList and related types
// ----------------------------------------------------------------------

/// A list of [`Entity`] types.
pub trait EntityList {
    /// The first entity in this list, or a sentinel for an empty list.
    type ListHead: Entity;
    /// An [`EntityList`] instance containing all entities but the first.
    type ListTail: EntityList;

    /// For recursion control: is this an empty list?
    const IS_EMPTY: bool = false;

    /// Accept a visitor to iterate across all entities in this list.
    fn accept_visitor<EV: EntityVisitor>(visitor: &mut EV);
}

mod entity_list;