microrm 0.6.3

Lightweight ORM using sqlite as a backend
Documentation
use std::marker::PhantomData;

use crate::schema::{
    self,
    datum::OwnedDatumList,
    entity::{
        Entity, EntityID, EntityPart, EntityPartList, EntityPartVisitor, EntityRef,
        EntityRefDatumList,
    },
};
use crate::DBResult;

#[derive(Debug)]
pub struct IPEntityID<OE: Entity>(pub i64, PhantomData<OE>);

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

impl<OE: Entity> Copy for IPEntityID<OE> {}

impl<OE: Entity> PartialEq for IPEntityID<OE> {
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl<OE: Entity> Eq for IPEntityID<OE> {}

impl<OE: Entity> Ord for IPEntityID<OE> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<OE: Entity> PartialOrd for IPEntityID<OE> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<OE: Entity> std::hash::Hash for IPEntityID<OE> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state)
    }
}

impl<OE: Entity> schema::datum::Datum for IPEntityID<OE> {
    fn sql_type() -> &'static str {
        i64::sql_type()
    }
    fn bind_to(&self, stmt: &mut crate::db::StatementContext, index: i32) -> DBResult<()> {
        self.0.bind_to(stmt, index)
    }
    fn build_from(
        adata: schema::relation::RelationData,
        stmt: &mut crate::db::StatementRow,
        index: &mut i32,
    ) -> DBResult<Self>
    where
        Self: Sized,
    {
        Ok(Self(i64::build_from(adata, stmt, index)?, PhantomData))
    }
    fn accept_discriminator(d: &mut impl schema::datum::DatumDiscriminator)
    where
        Self: Sized,
    {
        d.visit_entity_id::<IPEntity<OE>>();
    }
    fn accept_discriminator_ref(&self, d: &mut impl schema::datum::DatumDiscriminatorRef)
    where
        Self: Sized,
    {
        d.visit_entity_id::<IPEntity<OE>>(self);
    }
}

impl<OE: Entity> schema::datum::OwnedDatum for IPEntityID<OE> {
    type RefData<'a> = Self;
    fn as_ref(&self) -> Self::RefData<'_> {
        *self
    }
}
impl<OE: Entity> schema::datum::BorrowedDatum<'_, IPEntityID<OE>> for IPEntityID<OE> {
    fn as_owned(&self) -> Self {
        *self
    }
}

impl<OE: Entity> EntityID for IPEntityID<OE> {
    type Entity = IPEntity<OE>;

    fn from_raw(id: i64) -> Self {
        Self(id, PhantomData)
    }
    fn into_raw(self) -> i64 {
        self.0
    }
}

pub struct IPEntityIDPart<OE: Entity>(PhantomData<OE>);
impl<OE: Entity> Clone for IPEntityIDPart<OE> {
    fn clone(&self) -> Self {
        *self
    }
}
impl<OE: Entity> Copy for IPEntityIDPart<OE> {}
impl<OE: Entity> Default for IPEntityIDPart<OE> {
    fn default() -> Self {
        Self(PhantomData)
    }
}

impl<OE: Entity> EntityPart for IPEntityIDPart<OE> {
    type Datum = IPEntityID<OE>;
    type Entity = IPEntity<OE>;
    fn desc() -> Option<&'static str> {
        None
    }
    fn unique() -> bool {
        false
    }
    fn part_name() -> &'static str {
        "id"
    }
    fn get_datum(_: &Self::Entity) -> &Self::Datum {
        unreachable!()
    }
}

pub struct IPEntityPart<OE: Entity, OEP: EntityPart<Entity = OE>>(PhantomData<OEP>);
impl<OE: Entity, OEP: EntityPart<Entity = OE>> Clone for IPEntityPart<OE, OEP> {
    fn clone(&self) -> Self {
        Self(PhantomData)
    }
}
impl<OE: Entity, OEP: EntityPart<Entity = OE>> Default for IPEntityPart<OE, OEP> {
    fn default() -> Self {
        Self(PhantomData)
    }
}

impl<OE: Entity, OEP: EntityPart<Entity = OE>> EntityPart for IPEntityPart<OE, OEP> {
    type Datum = OEP::Datum;
    type Entity = IPEntity<OE>;
    fn desc() -> Option<&'static str> {
        OEP::desc()
    }
    fn unique() -> bool {
        OEP::unique()
    }
    fn part_name() -> &'static str {
        OEP::part_name()
    }
    fn get_datum(from: &Self::Entity) -> &Self::Datum {
        OEP::get_datum(&from.0)
    }
}

pub struct IPEntityPartList<OE: Entity, OEPL: EntityPartList<Entity = OE>>(PhantomData<OEPL>);

struct PartListVisitorWrapper<'a, OE: Entity, EPV: EntityPartVisitor<Entity = IPEntity<OE>>>(
    &'a mut EPV,
);

impl<OE: Entity, EPV: EntityPartVisitor<Entity = IPEntity<OE>>> EntityPartVisitor
    for PartListVisitorWrapper<'_, OE, EPV>
{
    type Entity = OE;

    // this is only for the datum versions
    fn visit<EP: EntityPart<Entity = Self::Entity>>(&mut self) {
        unreachable!()
    }

    fn visit_datum<EP: EntityPart<Entity = Self::Entity>>(
        &mut self,
        datum: &EP::Datum,
    ) -> DBResult<()> {
        self.0.visit_datum::<IPEntityPart<OE, EP>>(datum)
    }
    fn visit_datum_mut<EP: EntityPart<Entity = Self::Entity>>(&mut self, datum: &mut EP::Datum) {
        self.0.visit_datum_mut::<IPEntityPart<OE, EP>>(datum);
    }
}

impl<OE: Entity, OEPL: EntityPartList<Entity = OE>> EntityPartList for IPEntityPartList<OE, OEPL> {
    type Entity = IPEntity<OE>;
    type ListHead = IPEntityPart<OE, OEPL::ListHead>;
    type ListTail = IPEntityPartList<OE, OEPL::ListTail>;
    type DatumList = OEPL::DatumList;

    fn build_datum_list(stmt: &mut crate::db::StatementRow) -> DBResult<Self::DatumList> {
        OEPL::build_datum_list(stmt)
    }
    fn build_datum_ref_list<'l, 'm>(
        stmt: &'l mut crate::db::StatementRow,
    ) -> DBResult<<Self::DatumList as OwnedDatumList>::RefList<'m>>
    where
        'l: 'm,
    {
        OEPL::build_datum_ref_list(stmt)
    }
    fn accept_part_visitor(
        visitor: &mut impl schema::entity::EntityPartVisitor<Entity = Self::Entity>,
    ) {
        if !OEPL::IS_EMPTY {
            visitor.visit::<Self::ListHead>();
            Self::ListTail::accept_part_visitor(visitor);
        }
    }
    fn accept_part_visitor_ref(
        datum_list: &Self::DatumList,
        visitor: &mut impl schema::entity::EntityPartVisitor<Entity = Self::Entity>,
    ) -> DBResult<()> {
        OEPL::accept_part_visitor_ref(datum_list, &mut PartListVisitorWrapper(visitor))
    }
}

#[derive(Debug)]
pub struct IPEntity<OE: Entity>(pub OE);

lazy_static::lazy_static! {
    static ref IP_NAME_MAP: std::sync::RwLock<std::collections::HashMap<&'static str, &'static str>> = {
        std::sync::RwLock::new(std::collections::HashMap::new())
    };
}

pub struct IPERef<'l, OE: Entity> {
    data: OE::ERef<'l>,
}

impl<'l, OE: Entity> IPERef<'l, OE> {
    pub fn new(data: OE::ERef<'l>) -> Self {
        Self { data }
    }
}

// XXX: implement borrow/deref for IPERef

impl<OE: Entity> Clone for IPERef<'_, OE> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<OE: Entity> Copy for IPERef<'_, OE> {}

impl<'l, OE: Entity> EntityRef<'l> for IPERef<'l, OE> {
    type Entity = IPEntity<OE>;
    // type BorrowedList = <OE::ERef<'l> as EntityRef<'l>>::BorrowedList; // <<IPEntity<OE> as Entity>::ERef<'l> as EntityRef<'l>>::BorrowedList;
    fn as_entity(&self) -> Self::Entity {
        IPEntity(self.data.as_entity())
    }

    fn as_borrowed_list<'r>(&self) -> EntityRefDatumList<'r, OE>
    where
        'l: 'r,
    {
        todo!()
    }

    fn from_borrowed_list<'r>(_l: EntityRefDatumList<'r, OE>) -> Self
    where
        'r: 'l,
    {
        todo!()
    }
}

impl<OE: Entity> Entity for IPEntity<OE> {
    type ID = IPEntityID<OE>;
    type Parts = IPEntityPartList<OE, OE::Parts>;
    type Keys = IPEntityPartList<OE, OE::Keys>;
    type IDPart = IPEntityIDPart<OE>;
    type ERef<'a>
        = IPERef<'a, OE>
    where
        Self: Sized;

    fn as_ref(&self) -> Self::ERef<'_>
    where
        Self: Sized,
    {
        IPERef {
            data: self.0.as_ref(),
        }
    }

    fn entity_name() -> &'static str {
        // try lookup
        let nmap = IP_NAME_MAP.read().unwrap();
        if let Some(ename) = nmap.get(OE::entity_name()) {
            ename
        } else {
            drop(nmap);
            let mut nwmap = IP_NAME_MAP.write().unwrap();
            let ename: &'static str =
                Box::leak(format!("_IP_{}", OE::entity_name()).into_boxed_str());
            nwmap.insert(OE::entity_name(), ename);
            ename
        }
    }

    fn build(values: <Self::Parts as EntityPartList>::DatumList) -> Self {
        Self(OE::build(values))
    }

    fn accept_part_visitor(visitor: &mut impl schema::entity::EntityPartVisitor<Entity = Self>) {
        Self::Parts::accept_part_visitor(visitor)
    }

    fn accept_part_visitor_ref(
        &self,
        visitor: &mut impl schema::entity::EntityPartVisitor<Entity = Self>,
    ) -> DBResult<()> {
        self.0
            .accept_part_visitor_ref(&mut PartListVisitorWrapper(visitor))
    }

    fn accept_part_visitor_mut(
        &mut self,
        visitor: &mut impl schema::entity::EntityPartVisitor<Entity = Self>,
    ) {
        self.0
            .accept_part_visitor_ref(&mut PartListVisitorWrapper(visitor))
            .expect("migration accept_part_visitor_mut failed");
    }
}