atmosphere_core/
rel.rs

1//! Provides traits for managing relationships between database entities.
2//!
3//! This module contains traits and their implementations for handling relationships such as
4//! 'RefersTo' and 'ReferredBy'. These traits facilitate operations like resolving and deleting
5//! relationships in a database using SQLx.
6
7use async_trait::async_trait;
8use sqlx::database::Database;
9use sqlx::{Executor, IntoArguments};
10
11use crate::bind::Bind;
12use crate::query::{Query, QueryError};
13use crate::runtime::sql;
14use crate::schema::Table;
15use crate::{Error, ForeignKey, Result};
16
17/// Defines a relationship where `Self` refers to `Other`.
18///
19/// Implements functionality to resolve this relationship, fetching the `Other` entity that `Self`
20/// refers to.
21#[async_trait]
22pub trait RefersTo<Other>
23where
24    Self: Table + Bind,
25    Other: Table + Bind + Unpin + Sync,
26{
27    const FOREIGN_KEY: ForeignKey<Self>;
28
29    /// Asynchronously resolves and retrieves the `Other` entity that `Self` refers to from the
30    /// database.
31    async fn resolve<'e, E>(&self, executor: E) -> Result<Other>
32    where
33        E: Executor<'e, Database = crate::Driver>,
34        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
35    {
36        let Query { builder, .. } = sql::select::<Other>();
37
38        let mut query = sqlx::query_as(builder.sql());
39
40        let fk = Self::FOREIGN_KEY.as_col();
41        query = self.bind(&fk, query).unwrap();
42
43        query
44            .persistent(false)
45            .fetch_one(executor)
46            .await
47            .map_err(QueryError::from)
48            .map_err(Error::Query)
49    }
50}
51
52/// Defines a relationship where `Self` is referred to by many `Other`.
53///
54/// This trait provides methods to resolve these relationships, including fetching all `Other`
55/// entities referring to `Self`, resolving by primary key, and deleting all such referring
56/// entities.
57#[async_trait]
58pub trait ReferredBy<Other>
59where
60    Self: Table + Bind + Unpin + Sync,
61    Other: Table + Bind + RefersTo<Self> + Unpin + Sync,
62{
63    /// Asynchronously fetches all `Other` entities referring to `Self`.
64    async fn resolve<'e, E>(&self, executor: E) -> Result<Vec<Other>>
65    where
66        E: Executor<'e, Database = crate::Driver>,
67        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
68    {
69        let Query { builder, .. } = sql::select_by::<Other>(Other::FOREIGN_KEY.as_col());
70
71        let mut query = sqlx::query_as(builder.sql());
72
73        let pk = Self::PRIMARY_KEY.as_col();
74        query = self.bind(&pk, query).unwrap();
75
76        query
77            .persistent(false)
78            .fetch_all(executor)
79            .await
80            .map_err(QueryError::from)
81            .map_err(Error::Query)
82    }
83
84    /// Resolves the referring entities based on the primary key of `Self`.
85    async fn resolve_by<'e, E>(executor: E, pk: &Self::PrimaryKey) -> Result<Vec<Other>>
86    where
87        E: Executor<'e, Database = crate::Driver>,
88        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
89    {
90        let Query { builder, .. } = sql::select_by::<Other>(Other::FOREIGN_KEY.as_col());
91
92        sqlx::query_as(builder.sql())
93            .bind(pk)
94            .persistent(false)
95            .fetch_all(executor)
96            .await
97            .map_err(QueryError::from)
98            .map_err(Error::Query)
99    }
100
101    /// Deletes all `Other` entities referring to `Self`.
102    async fn delete_all<'e, E>(
103        &self,
104        executor: E,
105    ) -> Result<<crate::Driver as sqlx::Database>::QueryResult>
106    where
107        E: Executor<'e, Database = crate::Driver>,
108        for<'q> <crate::Driver as Database>::Arguments<'q>: IntoArguments<'q, crate::Driver> + Send,
109    {
110        let Query { builder, .. } = sql::delete_by::<Other>(Other::FOREIGN_KEY.as_col());
111
112        let mut query = sqlx::query(builder.sql());
113
114        let pk = Self::PRIMARY_KEY.as_col();
115        query = self.bind(&pk, query).unwrap();
116
117        query
118            .persistent(false)
119            .execute(executor)
120            .await
121            .map_err(QueryError::from)
122            .map_err(Error::Query)
123    }
124}