sea_orm/entity/compound/
belongs_to.rs1use 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 pub fn is_unloaded(&self) -> bool {
94 matches!(self, Self::Unloaded)
95 }
96
97 pub fn is_loaded(&self) -> bool {
99 matches!(self, Self::Loaded(_))
100 }
101}
102
103impl<E> BelongsTo<E>
104where
105 E: EntityTrait,
106{
107 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 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 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 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 pub fn is_not_found(&self) -> bool {
148 matches!(self, Self::Loaded(None))
149 }
150
151 pub fn is_unloaded_or_not_found(&self) -> bool {
154 matches!(self, Self::Unloaded | Self::Loaded(None))
155 }
156
157 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 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 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 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}