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,
};
pub trait Relation<U: ToTable> {
type Target;
type Reverse;
fn from_rows(rows: Vec<tokio_postgres::Row>) -> Self::Reverse;
}
#[derive(Debug, Clone, Copy)]
pub struct OneToOne<T: ToTable> {
_phantom: PhantomData<T>,
id: i32,
}
impl<T: ToTable> OneToOne<T> {
pub fn new(id: i32) -> OneToOne<T> {
OneToOne {
_phantom: PhantomData,
id,
}
}
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)?))
}
}
#[derive(Debug, Copy, Clone)]
pub struct ManyToOne<T: ToTable> {
_phantom: PhantomData<T>,
id: i32,
}
impl<T: ToTable> ManyToOne<T> {
pub fn new(id: i32) -> ManyToOne<T> {
ManyToOne {
_phantom: PhantomData,
id,
}
}
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)
}
}