use crate::model::Model;
use std::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RelationKind {
ForeignKey,
OneToOne,
}
#[derive(Debug, Clone, Copy)]
pub struct RelationPath<Source: Model, Target: Model> {
pub(crate) source_column: &'static str,
pub(crate) target_table: &'static str,
pub(crate) kind: RelationKind,
_markers: PhantomData<fn() -> (Source, Target)>,
}
impl<Source: Model, Target: Model> RelationPath<Source, Target> {
pub(crate) const fn new(
source_column: &'static str,
target_table: &'static str,
kind: RelationKind,
) -> Self {
Self {
source_column,
target_table,
kind,
_markers: PhantomData,
}
}
#[inline]
pub fn source_column(&self) -> &'static str {
self.source_column
}
#[inline]
pub fn target_table(&self) -> &'static str {
self.target_table
}
#[inline]
pub fn kind(&self) -> RelationKind {
self.kind
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::DjogiError;
use crate::descriptor::ModelDescriptor;
use crate::model::Model;
use crate::types::HeerId;
use std::future::Future;
struct Src;
struct Dst;
macro_rules! dummy_model {
($ty:ty, $table:literal) => {
impl crate::model::__sealed::Sealed for $ty {}
impl Model for $ty {
type Pk = HeerId;
type Fields = ();
fn table_name() -> &'static str {
$table
}
fn pk_value(&self) -> &Self::Pk {
unreachable!()
}
fn descriptor() -> &'static ModelDescriptor {
unreachable!()
}
fn get(
_ctx: &mut crate::context::DjogiContext,
_id: Self::Pk,
) -> impl Future<Output = Result<Self, DjogiError>> + Send {
async { unreachable!() }
}
fn create(
_ctx: &mut crate::context::DjogiContext,
_v: Self,
) -> impl Future<Output = Result<Self, DjogiError>> + Send {
async { unreachable!() }
}
fn save<'ctx>(
&'ctx mut self,
_ctx: &'ctx mut crate::context::DjogiContext,
) -> impl Future<Output = Result<(), DjogiError>> + Send + 'ctx {
async { unreachable!() }
}
fn delete(
self,
_ctx: &mut crate::context::DjogiContext,
) -> impl Future<Output = Result<(), DjogiError>> + Send {
async { unreachable!() }
}
fn refresh_from_db<'ctx>(
&'ctx self,
_ctx: &'ctx mut crate::context::DjogiContext,
) -> impl Future<Output = Result<Self, DjogiError>> + Send + 'ctx {
async { unreachable!() }
}
}
};
}
dummy_model!(Src, "srcs");
dummy_model!(Dst, "dsts");
#[test]
fn relation_path_holds_source_column_and_target_table() {
let p: RelationPath<Src, Dst> =
RelationPath::new("dst_id", "dsts", RelationKind::ForeignKey);
assert_eq!(p.source_column(), "dst_id");
assert_eq!(p.target_table(), "dsts");
assert_eq!(p.kind(), RelationKind::ForeignKey);
}
#[test]
fn relation_path_one_to_one_kind_round_trips() {
let p: RelationPath<Src, Dst> = RelationPath::new("dst_id", "dsts", RelationKind::OneToOne);
assert_eq!(p.kind(), RelationKind::OneToOne);
}
#[test]
fn relation_path_is_zst_shaped() {
use std::mem::size_of;
assert_eq!(
size_of::<RelationPath<Src, Dst>>(),
size_of::<(&'static str, &'static str, RelationKind)>()
);
}
}