Skip to main content

sea_orm/entity/compound/
belongs_to.rs

1use crate::{ActiveBelongsTo, EntityTrait};
2
3#[doc(hidden)]
4pub trait BelongsToCardinality {
5    type Entity: EntityTrait;
6    type Loaded;
7    type Set;
8    type Value<M>;
9
10    fn into_loaded<M>(value: Self::Value<M>) -> Self::Loaded
11    where
12        M: Into<<Self::Entity as EntityTrait>::ModelEx>;
13
14    fn into_set<M>(value: Self::Value<M>) -> Self::Set
15    where
16        M: Into<<Self::Entity as EntityTrait>::ActiveModelEx>;
17}
18
19impl<E> BelongsToCardinality for E
20where
21    E: EntityTrait,
22{
23    type Entity = E;
24    type Loaded = Box<E::ModelEx>;
25    type Set = Box<E::ActiveModelEx>;
26    type Value<M> = M;
27
28    fn into_loaded<M>(value: M) -> Self::Loaded
29    where
30        M: Into<E::ModelEx>,
31    {
32        Box::new(value.into())
33    }
34
35    fn into_set<M>(value: M) -> Self::Set
36    where
37        M: Into<E::ActiveModelEx>,
38    {
39        Box::new(value.into())
40    }
41}
42
43impl<E> BelongsToCardinality for Option<E>
44where
45    E: EntityTrait,
46{
47    type Entity = E;
48    type Loaded = Option<Box<E::ModelEx>>;
49    type Set = Option<Box<E::ActiveModelEx>>;
50    type Value<M> = Option<M>;
51
52    fn into_loaded<M>(value: Option<M>) -> Self::Loaded
53    where
54        M: Into<E::ModelEx>,
55    {
56        value.map(|model| Box::new(model.into()))
57    }
58
59    fn into_set<M>(value: Option<M>) -> Self::Set
60    where
61        M: Into<E::ActiveModelEx>,
62    {
63        value.map(|model| Box::new(model.into()))
64    }
65}
66
67#[derive_where::derive_where(
68    Debug, Clone, PartialEq, Eq, Hash;
69    <T as BelongsToCardinality>::Loaded
70)]
71#[derive(Default)]
72pub enum BelongsTo<T>
73where
74    T: BelongsToCardinality,
75{
76    #[default]
77    Unloaded,
78    Loaded(<T as BelongsToCardinality>::Loaded),
79}
80
81impl<T> BelongsTo<T>
82where
83    T: BelongsToCardinality,
84{
85    pub fn loaded<M>(model: T::Value<M>) -> Self
86    where
87        M: Into<<<T as BelongsToCardinality>::Entity as EntityTrait>::ModelEx>,
88    {
89        Self::Loaded(T::into_loaded(model))
90    }
91
92    /// Return true if variant is `Unloaded`
93    pub fn is_unloaded(&self) -> bool {
94        matches!(self, Self::Unloaded)
95    }
96
97    /// Return true if variant is `Loaded`
98    pub fn is_loaded(&self) -> bool {
99        matches!(self, Self::Loaded(_))
100    }
101}
102
103impl<E> BelongsTo<E>
104where
105    E: EntityTrait,
106{
107    /// Get a reference, if loaded
108    pub fn as_ref(&self) -> Option<&E::ModelEx> {
109        match self {
110            Self::Loaded(model) => Some(model.as_ref()),
111            Self::Unloaded => None,
112        }
113    }
114
115    /// Get a mutable reference, if loaded
116    pub fn as_mut(&mut self) -> Option<&mut E::ModelEx> {
117        match self {
118            Self::Loaded(model) => Some(model),
119            Self::Unloaded => None,
120        }
121    }
122
123    /// Convert into an `Option<ModelEx>`
124    pub fn into_option(self) -> Option<E::ModelEx> {
125        match self {
126            Self::Loaded(model) => Some(*model),
127            Self::Unloaded => None,
128        }
129    }
130
131    /// # Panics
132    ///
133    /// Panics if called on an `Unloaded` value.
134    pub fn unwrap(self) -> E::ModelEx {
135        match self {
136            Self::Loaded(model) => *model,
137            Self::Unloaded => panic!("called `BelongsTo::unwrap()` on an `Unloaded` value"),
138        }
139    }
140}
141
142impl<E> BelongsTo<Option<E>>
143where
144    E: EntityTrait,
145{
146    /// Return true if this optional relation was loaded and no model was found.
147    pub fn is_not_found(&self) -> bool {
148        matches!(self, Self::Loaded(None))
149    }
150
151    /// Return true if this optional relation holds no model — i.e. it is either
152    /// `Unloaded` or was loaded with no match (`is_not_found`).
153    pub fn is_unloaded_or_not_found(&self) -> bool {
154        matches!(self, Self::Unloaded | Self::Loaded(None))
155    }
156
157    /// Get a reference, if loaded with a model
158    pub fn as_ref(&self) -> Option<&E::ModelEx> {
159        match self {
160            Self::Loaded(Some(model)) => Some(model.as_ref()),
161            Self::Unloaded | Self::Loaded(None) => None,
162        }
163    }
164
165    /// Get a mutable reference, if loaded with a model
166    pub fn as_mut(&mut self) -> Option<&mut E::ModelEx> {
167        match self {
168            Self::Loaded(Some(model)) => Some(model),
169            Self::Unloaded | Self::Loaded(None) => None,
170        }
171    }
172
173    /// Convert into an `Option<ModelEx>`
174    pub fn into_option(self) -> Option<E::ModelEx> {
175        match self {
176            Self::Loaded(Some(model)) => Some(*model),
177            Self::Unloaded | Self::Loaded(None) => None,
178        }
179    }
180
181    /// # Panics
182    ///
183    /// Panics if called on an `Unloaded` or `Loaded(None)` value.
184    pub fn unwrap(self) -> E::ModelEx {
185        match self {
186            Self::Loaded(Some(model)) => *model,
187            Self::Unloaded => panic!("called `BelongsTo::unwrap()` on an `Unloaded` value"),
188            Self::Loaded(None) => panic!("called `BelongsTo::unwrap()` on a `Loaded(None)` value"),
189        }
190    }
191}
192
193impl<E> BelongsTo<E>
194where
195    E: EntityTrait,
196    E::ActiveModelEx: From<E::ModelEx>,
197{
198    pub fn into_active_model(self) -> ActiveBelongsTo<E> {
199        match self {
200            Self::Loaded(model) => ActiveBelongsTo::set(*model),
201            Self::Unloaded => ActiveBelongsTo::NotSet,
202        }
203    }
204}
205
206impl<E> BelongsTo<Option<E>>
207where
208    E: EntityTrait,
209    E::ActiveModelEx: From<E::ModelEx>,
210{
211    pub fn into_active_model(self) -> ActiveBelongsTo<Option<E>> {
212        match self {
213            Self::Loaded(Some(model)) => ActiveBelongsTo::set(Some(*model)),
214            Self::Unloaded | Self::Loaded(None) => ActiveBelongsTo::NotSet,
215        }
216    }
217}
218
219impl<E: EntityTrait> From<BelongsTo<E>> for Option<Box<E::ModelEx>> {
220    fn from(value: BelongsTo<E>) -> Self {
221        match value {
222            BelongsTo::Loaded(model) => Some(model),
223            BelongsTo::Unloaded => None,
224        }
225    }
226}
227
228impl<E: EntityTrait> From<BelongsTo<Option<E>>> for Option<Box<E::ModelEx>> {
229    fn from(value: BelongsTo<Option<E>>) -> Self {
230        match value {
231            BelongsTo::Loaded(model) => model,
232            BelongsTo::Unloaded => None,
233        }
234    }
235}
236
237impl<E: EntityTrait> From<Option<Box<E::ModelEx>>> for BelongsTo<E> {
238    fn from(value: Option<Box<E::ModelEx>>) -> Self {
239        match value {
240            Some(model) => Self::Loaded(model),
241            None => Self::Unloaded,
242        }
243    }
244}
245
246impl<E: EntityTrait> From<Option<Box<E::ModelEx>>> for BelongsTo<Option<E>> {
247    fn from(value: Option<Box<E::ModelEx>>) -> Self {
248        Self::Loaded(value)
249    }
250}
251
252#[cfg(feature = "with-json")]
253impl<T> serde::Serialize for BelongsTo<T>
254where
255    T: BelongsToCardinality,
256    <T as BelongsToCardinality>::Loaded: serde::Serialize,
257{
258    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
259    where
260        S: serde::Serializer,
261    {
262        match self {
263            BelongsTo::Unloaded => None,
264            BelongsTo::Loaded(model) => Some(model),
265        }
266        .serialize(serializer)
267    }
268}
269
270#[cfg(feature = "with-json")]
271impl<'de, E> serde::Deserialize<'de> for BelongsTo<E>
272where
273    E: EntityTrait,
274    E::ModelEx: serde::Deserialize<'de>,
275{
276    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
277    where
278        D: serde::Deserializer<'de>,
279    {
280        match <Option<E::ModelEx>>::deserialize(deserializer)? {
281            Some(model) => Ok(BelongsTo::Loaded(Box::new(model))),
282            None => Ok(BelongsTo::Unloaded),
283        }
284    }
285}
286
287#[cfg(feature = "with-json")]
288impl<'de, E> serde::Deserialize<'de> for BelongsTo<Option<E>>
289where
290    E: EntityTrait,
291    E::ModelEx: serde::Deserialize<'de>,
292{
293    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
294    where
295        D: serde::Deserializer<'de>,
296    {
297        match <Option<E::ModelEx>>::deserialize(deserializer)? {
298            Some(model) => Ok(BelongsTo::Loaded(Some(Box::new(model)))),
299            None => Ok(BelongsTo::Unloaded),
300        }
301    }
302}