ergol 0.1.9

an async ORM for Rust
Documentation
//! This module contains the struct that we use to represent the relationships between linked
//! tables.

use std::marker::PhantomData;

use bytes::BytesMut;

use tokio_postgres::types::{to_sql_checked, FromSql, IsNull, ToSql, Type};
use tokio_postgres::GenericClient;

use crate::{pg::Pg, Queryable, ToTable};

#[cfg(feature = "with-serde")]
use serde::{
    de::{self, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};

/// The different types of relation that tables can have are managed with this trait.
pub trait Relation<U: ToTable> {
    /// The type to which your struct will be linked.
    type Target;

    /// The type to which the linked struct will be linked.
    type Reverse;

    /// Convert rows into the reverse type.
    fn from_rows(rows: Vec<tokio_postgres::Row>) -> Self::Reverse;
}

/// A one to one relation ship.
///
/// You should not use this type by yourself, it should be automatically generated by the
/// `#[one_to_one]` macro attribute.
#[derive(Debug, Clone, Copy)]
pub struct OneToOne<T: ToTable> {
    _phantom: PhantomData<T>,
    /// The id of the referenced element.
    id: i32,
}

impl<T: ToTable> OneToOne<T> {
    /// Creates a one to one relation ship.
    pub fn new(id: i32) -> OneToOne<T> {
        OneToOne {
            _phantom: PhantomData,
            id,
        }
    }

    /// Fetches the referenced element.
    pub async fn fetch<Q: Queryable<impl GenericClient>>(
        &self,
        ergol: &Q,
    ) -> Result<T, tokio_postgres::Error> {
        let query = format!(
            "SELECT * FROM \"{}\" WHERE \"{}\" = $1",
            T::table_name(),
            T::id_name()
        );
        let mut rows = ergol.client().query(&query as &str, &[&self.id]).await?;
        let row = rows.pop().unwrap();
        Ok(<T as ToTable>::from_row(&row))
    }
}

impl<T: ToTable, U: ToTable> Relation<U> for OneToOne<T> {
    type Target = T;
    type Reverse = Option<U>;

    fn from_rows(mut rows: Vec<tokio_postgres::Row>) -> Self::Reverse {
        rows.pop().map(|x| <U as ToTable>::from_row(&x))
    }
}

impl<T: ToTable> Pg for OneToOne<T> {
    fn ty() -> String {
        format!(
            "INT UNIQUE NOT NULL REFERENCES \"{}\" (\"{}\") ON DELETE CASCADE",
            T::table_name(),
            T::id_name(),
        )
    }
}

impl<T: ToTable> From<T> for OneToOne<T> {
    fn from(t: T) -> OneToOne<T> {
        OneToOne::new(t.id())
    }
}

impl<T: ToTable> From<&T> for OneToOne<T> {
    fn from(t: &T) -> OneToOne<T> {
        OneToOne::new(t.id())
    }
}

impl<'a, T: ToTable> FromSql<'a> for OneToOne<T> {
    fn from_sql(
        ty: &Type,
        raw: &'a [u8],
    ) -> Result<Self, Box<dyn std::error::Error + 'static + Sync + Send>> {
        Ok(OneToOne::new(i32::from_sql(ty, raw)?))
    }

    fn accepts(ty: &Type) -> bool {
        <i32 as FromSql>::accepts(ty)
    }
}

impl<T: ToTable> ToSql for OneToOne<T> {
    fn to_sql(
        &self,
        ty: &Type,
        out: &mut BytesMut,
    ) -> Result<IsNull, Box<dyn std::error::Error + 'static + Sync + Send>> {
        self.id.to_sql(ty, out)
    }

    fn accepts(ty: &Type) -> bool {
        <i32 as ToSql>::accepts(ty)
    }

    to_sql_checked!();
}

#[cfg(feature = "with-serde")]
impl<T: ToTable> Serialize for OneToOne<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_i32(self.id)
    }
}

#[cfg(feature = "with-serde")]
impl<'de, T: ToTable> Deserialize<'de> for OneToOne<T> {
    fn deserialize<D>(deserializer: D) -> Result<OneToOne<T>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(OneToOne::new(deserializer.deserialize_i32(I32Visitor)?))
    }
}

/// A many to one relation ship.
///
/// You should not use this type by yourself, it should be automatically generated by the
/// `#[many_to_one]` macro attribute.
#[derive(Debug, Copy, Clone)]
pub struct ManyToOne<T: ToTable> {
    _phantom: PhantomData<T>,
    /// Creates a one to one relation ship.
    id: i32,
}

impl<T: ToTable> ManyToOne<T> {
    /// Creates a new many to one relationship.
    pub fn new(id: i32) -> ManyToOne<T> {
        ManyToOne {
            _phantom: PhantomData,
            id,
        }
    }

    /// Fetches the element referenced by this relationship.
    pub async fn fetch<Q: Queryable<impl GenericClient>>(
        &self,
        ergol: &Q,
    ) -> Result<T, tokio_postgres::Error> {
        let query = format!(
            "SELECT * FROM \"{}\" WHERE \"{}\" = $1",
            T::table_name(),
            T::id_name()
        );
        let mut rows = ergol.client().query(&query as &str, &[&self.id]).await?;
        let row = rows.pop().unwrap();
        Ok(<T as ToTable>::from_row(&row))
    }
}

impl<T: ToTable, U: ToTable> Relation<U> for ManyToOne<T> {
    type Target = T;
    type Reverse = Vec<U>;
    fn from_rows(rows: Vec<tokio_postgres::Row>) -> Self::Reverse {
        rows.into_iter()
            .map(|x| <U as ToTable>::from_row(&x))
            .collect()
    }
}

impl<T: ToTable> Pg for ManyToOne<T> {
    fn ty() -> String {
        format!(
            "INT NOT NULL REFERENCES \"{}\" (\"{}\") ON DELETE CASCADE",
            T::table_name(),
            T::id_name(),
        )
    }
}

impl<'a, T: ToTable> FromSql<'a> for ManyToOne<T> {
    fn from_sql(
        ty: &Type,
        raw: &'a [u8],
    ) -> Result<Self, Box<dyn std::error::Error + 'static + Sync + Send>> {
        Ok(ManyToOne::new(i32::from_sql(ty, raw)?))
    }

    fn accepts(ty: &Type) -> bool {
        <i32 as FromSql>::accepts(ty)
    }
}

impl<T: ToTable> ToSql for ManyToOne<T> {
    fn to_sql(
        &self,
        ty: &Type,
        out: &mut BytesMut,
    ) -> Result<IsNull, Box<dyn std::error::Error + 'static + Sync + Send>> {
        self.id.to_sql(ty, out)
    }

    fn accepts(ty: &Type) -> bool {
        <i32 as ToSql>::accepts(ty)
    }

    to_sql_checked!();
}

impl<T: ToTable> From<T> for ManyToOne<T> {
    fn from(t: T) -> ManyToOne<T> {
        ManyToOne::new(t.id())
    }
}

impl<T: ToTable> From<&T> for ManyToOne<T> {
    fn from(t: &T) -> ManyToOne<T> {
        ManyToOne::new(t.id())
    }
}

#[cfg(feature = "with-serde")]
impl<T: ToTable> Serialize for ManyToOne<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_i32(self.id)
    }
}

#[cfg(feature = "with-serde")]
impl<'de, T: ToTable> Deserialize<'de> for ManyToOne<T> {
    fn deserialize<D>(deserializer: D) -> Result<ManyToOne<T>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(ManyToOne::new(deserializer.deserialize_i32(I32Visitor)?))
    }
}

#[cfg(feature = "with-serde")]
struct I32Visitor;

#[cfg(feature = "with-serde")]
impl<'de> Visitor<'de> for I32Visitor {
    type Value = i32;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("an integer between -2^31 and 2^31")
    }

    fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(value)
    }
}