microrm 0.6.3

Lightweight ORM using sqlite as a backend
Documentation
// query implementations on schema types

use crate::{
    db::{StatementContext, Transaction},
    query::{self, Insertable, Query, QueryPart, Queryable, RelationInterface},
    schema::{
        entity::Entity,
        relation::{LocalSide, Relation, RelationData, RelationDomain, RelationMap, RelationRange},
        IDMap, Stored,
    },
    DBResult, Error,
};

// ----------------------------------------------------------------------
// Stored
// ----------------------------------------------------------------------

impl<T: Entity> Stored<T> {
    /// Synchronize the wrapped value with the corresponding database row.
    pub fn sync(&mut self, txn: &mut Transaction) -> DBResult<()> {
        query::base_queries::update_entity(txn, self)
    }
}

// ----------------------------------------------------------------------
// IDMap
// ----------------------------------------------------------------------

impl<T: Entity> IDMap<T> {
    /// Look up an Entity in this map by ID.
    pub fn by_id(&self, lease: &mut Transaction, id: T::ID) -> DBResult<Option<Stored<T>>> {
        self.with(T::IDPart::default(), id).first().get(lease)
    }
}

impl<T: Entity> Queryable for &IDMap<T> {
    type EntityOutput = T;
    type OutputContainer = Vec<Stored<T>>;
    type StaticVersion = &'static IDMap<T>;

    fn build(&self) -> DBResult<Query<'_>> {
        Ok(Query::new()
            .attach(QueryPart::Root, "SELECT DISTINCT")
            .attach(QueryPart::Columns, "*")
            .attach(QueryPart::From, format!("`{}`", T::entity_name())))
    }
    fn bind(&self, _stmt: &mut StatementContext, _index: &mut i32) -> DBResult<()> {
        Ok(())
    }
}

impl<T: Entity> Insertable<T> for IDMap<T> {
    fn insert(&self, txn: &mut Transaction, value: T) -> DBResult<T::ID> {
        let out = query::base_queries::insert(txn, &value)?;
        Ok(out)
    }

    fn insert_ref(&self, txn: &mut Transaction, value: T::ERef<'_>) -> DBResult<T::ID> {
        let out = query::base_queries::insert_ref::<T>(txn, value)?;
        Ok(out)
    }

    fn insert_and_return(&self, txn: &mut Transaction, value: T) -> DBResult<Stored<T>> {
        query::base_queries::insert_and_return(txn, value)
    }
}

impl<T: Entity> Insertable<T> for &IDMap<T> {
    fn insert(&self, lease: &mut Transaction, value: T) -> DBResult<T::ID> {
        <IDMap<T> as Insertable<T>>::insert(self, lease, value)
    }

    fn insert_ref(&self, lease: &mut Transaction, value: T::ERef<'_>) -> DBResult<T::ID> {
        <IDMap<T> as Insertable<T>>::insert_ref(self, lease, value)
    }

    fn insert_and_return(&self, lease: &mut Transaction, value: T) -> DBResult<Stored<T>> {
        <IDMap<T> as Insertable<T>>::insert_and_return(self, lease, value)
    }
}

// ----------------------------------------------------------------------
// RelationMap
// ----------------------------------------------------------------------

impl<T: Entity> RelationInterface for RelationMap<T> {
    type RemoteEntity = T;
    type StaticVersion = Self;
    const SIDE: LocalSide = LocalSide::Domain;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        self.data
            .as_ref()
            .ok_or(Error::LogicError(
                "no distinguishing name for empty RelationMap",
            ))
            .map(|d| d.part_name)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        self.data
            .as_ref()
            .ok_or(Error::LogicError("Reading from unassigned RelationMap"))
    }
}

impl<T: Entity> RelationInterface for &RelationMap<T> {
    type RemoteEntity = T;
    type StaticVersion = RelationMap<T>;
    const SIDE: LocalSide = LocalSide::Domain;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        <RelationMap<T> as RelationInterface>::get_distinguishing_name(self)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        <RelationMap<T> as RelationInterface>::get_data(self)
    }
}

// ----------------------------------------------------------------------
// RelationDomain
// ----------------------------------------------------------------------

impl<R: Relation> RelationInterface for RelationDomain<R> {
    type RemoteEntity = R::Range;
    type StaticVersion = Self;
    const SIDE: LocalSide = LocalSide::Domain;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        Ok(R::NAME)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        self.data
            .as_ref()
            .ok_or(Error::LogicError("Reading from unassigned RelationDomain"))
    }
}

impl<R: Relation> RelationInterface for &RelationDomain<R> {
    type RemoteEntity = R::Range;
    type StaticVersion = RelationDomain<R>;
    const SIDE: LocalSide = LocalSide::Domain;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        <RelationDomain<R> as RelationInterface>::get_distinguishing_name(self)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        <RelationDomain<R> as RelationInterface>::get_data(self)
    }
}

// ----------------------------------------------------------------------
// RelationRange
// ----------------------------------------------------------------------

impl<R: Relation> RelationInterface for RelationRange<R> {
    type RemoteEntity = R::Domain;
    type StaticVersion = Self;
    const SIDE: LocalSide = LocalSide::Range;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        Ok(R::NAME)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        self.data
            .as_ref()
            .ok_or(Error::LogicError("Reading from unassigned RelationRange"))
    }
}

impl<R: Relation> RelationInterface for &RelationRange<R> {
    type RemoteEntity = R::Domain;
    type StaticVersion = RelationRange<R>;
    const SIDE: LocalSide = LocalSide::Range;

    fn get_distinguishing_name(&self) -> DBResult<&'static str> {
        <RelationRange<R> as RelationInterface>::get_distinguishing_name(self)
    }

    fn get_data(&self) -> DBResult<&RelationData> {
        <RelationRange<R> as RelationInterface>::get_data(self)
    }
}