microrm 0.6.3

Lightweight ORM using sqlite as a backend
Documentation
use crate::{
    db::{StatementContext, StatementRow},
    schema::{
        datum::{BorrowedDatum, Datum, DatumDiscriminator, DatumDiscriminatorRef, OwnedDatum},
        entity::{Entity, EntityVisitor},
    },
    DBResult,
};

/// Represents an arbitrary relation between two types of entities.
///
/// In its most generic form, this represents a table that stores an arbitrary set of `(Domain::ID,
/// Range::ID)` pairs used to define the relation. Can be restricted to be injective if this is
/// desired. Doing so will incur a small runtime cost, since an extra index must be maintained.
pub trait Relation: 'static {
    /// The domain of the relation, aka the "pointed-from" type. This is interchangeable with
    /// `Range` unless `INJECTIVE` is set to true.
    type Domain: Entity;
    /// The range of the relation, aka the "pointed-to" type. This is interchangeable with
    /// `Domain` unless `INJECTIVE` is set to true.
    type Range: Entity;

    /// If true, then each Range-type entity can only be referred to by a single Domain-type
    /// entity. This roughly translates to the relation being computationally-efficiently
    /// invertible for a two-way map.
    const INJECTIVE: bool = false;

    /// A unique, human-readable name for the relation, which should be short and not include
    /// spaces.
    const NAME: &'static str;
}

pub(crate) trait RelationExt: Relation {
    fn try_coerce<Q: Relation>() -> DBResult<()>
    where
        Self: Sized,
    {
        if Self::NAME == Q::NAME
            && Self::Domain::entity_name() == Q::Domain::entity_name()
            && Self::Range::entity_name() == Q::Range::entity_name()
            && Self::INJECTIVE == Q::INJECTIVE
        {
            Ok(())
        } else {
            panic!(
                "Bad relation migration: types {} and {} have different semantics. Name: {}/{}, Domain: {}/{}, Range: {}/{}, injective: {}/{}",
                std::any::type_name::<Self>(),
                std::any::type_name::<Q>(),
                Self::NAME, Q::NAME,
                Self::Domain::entity_name(), Q::Domain::entity_name(),
                Self::Range::entity_name(), Q::Range::entity_name(),
                Self::INJECTIVE, Q::INJECTIVE
            );
        }
    }
}

impl<R: Relation> RelationExt for R {}

/// Enumeration used to represent which side of a relation a [`Relation`] trait implementation is representing.
#[derive(Debug)]
pub enum LocalSide {
    /// The side of the relation that the [`Relation`] represents is the `Domain` side.
    Domain,
    /// The side of the relation that the [`Relation`] represents is the `Range` side.
    Range,
}

/// Opaque data structure used for constructing `Relation{Map,Domain,Range}` instances.
#[derive(Clone, Copy)]
pub struct RelationData {
    pub(crate) local_name: &'static str,
    pub(crate) part_name: &'static str,
    pub(crate) local_id: i64,
}

/// Represents a simple one-to-many non-injective entity relationship.
///
/// This is a 'shortcut' type that doesn't require defining a tag type that implements `Relation`.
pub struct RelationMap<T: Entity> {
    pub(crate) data: Option<RelationData>,
    _ghost: std::marker::PhantomData<T>,
}

impl<T: Entity> Clone for RelationMap<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: Entity> Copy for RelationMap<T> {}

impl<T: Entity> std::fmt::Debug for RelationMap<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!(
            "RelationMap {{ id: {:?} }}",
            self.data.as_ref().map(|d| d.local_id)
        ))
    }
}

impl<T: Entity> Default for RelationMap<T> {
    fn default() -> Self {
        Self {
            data: None,
            _ghost: Default::default(),
        }
    }
}

impl<T: Entity> Datum for RelationMap<T> {
    const INTERNAL_DATUM: bool = false;

    fn sql_type() -> &'static str {
        unreachable!()
    }

    fn debug_field(&self, _field: &'static str, _fmt: &mut std::fmt::DebugStruct)
    where
        Self: Sized,
    {
    }

    fn accept_entity_visitor(v: &mut impl EntityVisitor) {
        v.visit::<T>();
    }

    fn accept_discriminator(d: &mut impl DatumDiscriminator)
    where
        Self: Sized,
    {
        d.visit_relation_map::<T>();
    }

    fn accept_discriminator_ref(&self, d: &mut impl DatumDiscriminatorRef)
    where
        Self: Sized,
    {
        d.visit_relation_map::<T>(self);
    }

    fn bind_to(&self, _stmt: &mut StatementContext, _index: i32) -> DBResult<()> {
        unreachable!()
    }

    fn build_from(rdata: RelationData, _stmt: &mut StatementRow, _index: &mut i32) -> DBResult<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            data: Some(rdata),
            _ghost: Default::default(),
        })
    }

    fn update_adata(&mut self, rdata: RelationData) {
        self.data = Some(rdata);
    }
}
impl<T: Entity> OwnedDatum for RelationMap<T> {
    type RefData<'a> = Self;
    fn as_ref(&self) -> Self::RefData<'_> {
        *self
    }
}
impl<T: Entity> BorrowedDatum<'_, RelationMap<T>> for RelationMap<T> {
    fn as_owned(&self) -> Self {
        *self
    }
}

/// The domain part of a full Relation definition.
pub struct RelationDomain<R: Relation> {
    pub(crate) data: Option<RelationData>,
    _ghost: std::marker::PhantomData<R>,
}

impl<R: Relation> Clone for RelationDomain<R> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<R: Relation> Copy for RelationDomain<R> {}

impl<R: Relation> Default for RelationDomain<R> {
    fn default() -> Self {
        Self {
            data: None,
            _ghost: Default::default(),
        }
    }
}

impl<R: Relation> std::fmt::Debug for RelationDomain<R> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!(
            "RelationDomain {{ id: {:?} }}",
            self.data.as_ref().map(|d| d.local_id)
        ))
    }
}

impl<R: Relation> Datum for RelationDomain<R> {
    const INTERNAL_DATUM: bool = false;
    fn sql_type() -> &'static str {
        unreachable!()
    }

    fn debug_field(&self, _field: &'static str, _fmt: &mut std::fmt::DebugStruct)
    where
        Self: Sized,
    {
    }

    fn accept_entity_visitor(v: &mut impl EntityVisitor) {
        v.visit::<R::Range>();
    }

    fn accept_discriminator(d: &mut impl DatumDiscriminator)
    where
        Self: Sized,
    {
        d.visit_relation_domain::<R>();
    }

    fn accept_discriminator_ref(&self, d: &mut impl DatumDiscriminatorRef)
    where
        Self: Sized,
    {
        d.visit_relation_domain(self);
    }

    fn bind_to(&self, _stmt: &mut StatementContext, _index: i32) -> DBResult<()> {
        unreachable!()
    }

    fn build_from(rdata: RelationData, _stmt: &mut StatementRow, _index: &mut i32) -> DBResult<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            data: Some(rdata),
            _ghost: Default::default(),
        })
    }

    fn update_adata(&mut self, rdata: RelationData) {
        self.data = Some(rdata);
    }
}
impl<R: Relation> OwnedDatum for RelationDomain<R> {
    type RefData<'a> = Self;
    fn as_ref(&self) -> Self::RefData<'_> {
        *self
    }
}
impl<R: Relation> BorrowedDatum<'_, RelationDomain<R>> for RelationDomain<R> {
    fn as_owned(&self) -> Self {
        *self
    }
}

/// The range part of a full Relation definition.
pub struct RelationRange<R: Relation> {
    pub(crate) data: Option<RelationData>,
    _ghost: std::marker::PhantomData<R>,
}

impl<R: Relation> Clone for RelationRange<R> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<R: Relation> Copy for RelationRange<R> {}

impl<R: Relation> Default for RelationRange<R> {
    fn default() -> Self {
        Self {
            data: None,
            _ghost: Default::default(),
        }
    }
}

impl<R: Relation> std::fmt::Debug for RelationRange<R> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!(
            "RelationRange {{ id: {:?} }}",
            self.data.as_ref().map(|d| d.local_id)
        ))
    }
}

impl<R: Relation> Datum for RelationRange<R> {
    const INTERNAL_DATUM: bool = false;
    fn sql_type() -> &'static str {
        unreachable!()
    }

    fn debug_field(&self, _field: &'static str, _fmt: &mut std::fmt::DebugStruct)
    where
        Self: Sized,
    {
    }

    fn accept_entity_visitor(v: &mut impl EntityVisitor) {
        v.visit::<R::Domain>();
    }

    fn accept_discriminator(d: &mut impl DatumDiscriminator)
    where
        Self: Sized,
    {
        d.visit_relation_range::<R>();
    }

    fn accept_discriminator_ref(&self, d: &mut impl DatumDiscriminatorRef)
    where
        Self: Sized,
    {
        d.visit_relation_range(self);
    }

    fn bind_to(&self, _stmt: &mut StatementContext, _index: i32) -> DBResult<()> {
        unreachable!()
    }

    fn build_from(rdata: RelationData, _stmt: &mut StatementRow, _index: &mut i32) -> DBResult<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            data: Some(rdata),
            _ghost: Default::default(),
        })
    }

    fn update_adata(&mut self, rdata: RelationData) {
        self.data = Some(rdata);
    }
}
impl<R: Relation> OwnedDatum for RelationRange<R> {
    type RefData<'a> = Self;
    fn as_ref(&self) -> Self::RefData<'_> {
        *self
    }
}
impl<R: Relation> BorrowedDatum<'_, RelationRange<R>> for RelationRange<R> {
    fn as_owned(&self) -> Self {
        *self
    }
}