1use super::compound::{BelongsTo, BelongsToCardinality, HasMany, HasOne};
2use crate::{ActiveModelTrait, DbErr, EntityTrait, ModelTrait, TryIntoModel};
3use core::ops::{Index, IndexMut};
4
5#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; <T as BelongsToCardinality>::Set)]
13#[derive(Default)]
14pub enum ActiveBelongsTo<T>
15where
16 T: BelongsToCardinality,
17{
18 #[default]
20 NotSet,
21 Set(<T as BelongsToCardinality>::Set),
23}
24
25#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; E::ActiveModelEx)]
31#[derive(Default)]
32pub enum ActiveHasOne<E>
33where
34 E: EntityTrait,
35{
36 #[default]
38 NotSet,
39 Set(Option<Box<E::ActiveModelEx>>),
41}
42
43#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; E::ActiveModelEx)]
50#[derive(Default)]
51#[non_exhaustive]
52pub enum ActiveHasMany<E: EntityTrait> {
53 #[default]
55 NotSet,
56 Replace(Vec<E::ActiveModelEx>),
59 Append(Vec<E::ActiveModelEx>),
62}
63
64#[derive(Debug, Copy, Clone, PartialEq, Eq)]
68pub enum ActiveModelAction {
69 Insert,
71 Update,
73 Save,
76}
77
78impl<T> ActiveBelongsTo<T>
79where
80 T: BelongsToCardinality,
81{
82 pub fn set<M>(model: T::Value<M>) -> Self
85 where
86 M: Into<<<T as BelongsToCardinality>::Entity as EntityTrait>::ActiveModelEx>,
87 {
88 Self::Set(T::into_set(model))
89 }
90
91 pub fn take(&mut self) -> Self {
93 std::mem::take(self)
94 }
95
96 pub fn is_not_set(&self) -> bool {
98 matches!(self, Self::NotSet)
99 }
100
101 pub fn is_set(&self) -> bool {
103 matches!(self, Self::Set(_))
104 }
105}
106
107impl<E> ActiveBelongsTo<E>
108where
109 E: EntityTrait,
110{
111 pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
113 match self {
114 Self::Set(model) => Some(model),
115 _ => None,
116 }
117 }
118
119 #[allow(clippy::should_implement_trait)]
121 pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
122 match self {
123 Self::Set(model) => Some(model),
124 _ => None,
125 }
126 }
127
128 pub fn is_changed(&self) -> bool {
130 match self {
131 Self::Set(model) => model.is_changed(),
132 _ => false,
133 }
134 }
135
136 pub fn into_option(self) -> Option<E::ActiveModelEx> {
138 match self {
139 Self::Set(model) => Some(*model),
140 Self::NotSet => None,
141 }
142 }
143
144 pub fn try_into_model(self) -> Result<BelongsTo<E>, DbErr>
146 where
147 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
148 {
149 Ok(match self {
150 Self::Set(model) => BelongsTo::Loaded(Box::new((*model).try_into_model()?)),
151 Self::NotSet => BelongsTo::Unloaded,
152 })
153 }
154}
155
156impl<E> ActiveBelongsTo<Option<E>>
157where
158 E: EntityTrait,
159{
160 pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
162 match self {
163 Self::Set(Some(model)) => Some(model),
164 _ => None,
165 }
166 }
167
168 #[allow(clippy::should_implement_trait)]
170 pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
171 match self {
172 Self::Set(Some(model)) => Some(model),
173 _ => None,
174 }
175 }
176
177 pub fn is_changed(&self) -> bool {
179 match self {
180 Self::Set(Some(model)) => model.is_changed(),
181 Self::Set(None) => true,
182 Self::NotSet => false,
183 }
184 }
185
186 pub fn into_option(self) -> Option<E::ActiveModelEx> {
188 match self {
189 Self::Set(Some(model)) => Some(*model),
190 Self::Set(None) | Self::NotSet => None,
191 }
192 }
193
194 pub fn try_into_model(self) -> Result<BelongsTo<Option<E>>, DbErr>
196 where
197 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
198 {
199 Ok(match self {
200 Self::Set(Some(model)) => BelongsTo::Loaded(Some(Box::new((*model).try_into_model()?))),
201 Self::Set(None) => BelongsTo::Loaded(None),
202 Self::NotSet => BelongsTo::Unloaded,
203 })
204 }
205}
206
207impl<E> ActiveHasOne<E>
208where
209 E: EntityTrait,
210{
211 pub fn set(model: Option<impl Into<E::ActiveModelEx>>) -> Self {
213 Self::Set(model.map(|model| Box::new(model.into())))
214 }
215
216 pub fn is_not_set(&self) -> bool {
218 matches!(self, Self::NotSet)
219 }
220
221 pub fn is_set(&self) -> bool {
223 matches!(self, Self::Set(_))
224 }
225
226 pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
228 match self {
229 Self::Set(Some(model)) => Some(model),
230 _ => None,
231 }
232 }
233
234 #[allow(clippy::should_implement_trait)]
236 pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
237 match self {
238 Self::Set(Some(model)) => Some(model),
239 _ => None,
240 }
241 }
242
243 pub fn is_changed(&self) -> bool {
245 match self {
246 Self::Set(Some(model)) => model.is_changed(),
247 Self::Set(None) => true,
248 Self::NotSet => false,
249 }
250 }
251
252 pub fn into_option(self) -> Option<E::ActiveModelEx> {
254 match self {
255 Self::Set(Some(model)) => Some(*model),
256 Self::Set(None) | Self::NotSet => None,
257 }
258 }
259
260 pub fn try_into_model(self) -> Result<HasOne<E>, DbErr>
262 where
263 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
264 {
265 Ok(match self {
266 Self::Set(Some(model)) => HasOne::Loaded(Some(Box::new((*model).try_into_model()?))),
267 Self::Set(None) => HasOne::Loaded(None),
268 Self::NotSet => HasOne::Unloaded,
269 })
270 }
271}
272
273impl<E> ActiveHasMany<E>
274where
275 E: EntityTrait,
276{
277 pub fn take(&mut self) -> Self {
279 std::mem::take(self)
280 }
281
282 pub fn as_slice(&self) -> &[E::ActiveModelEx] {
284 match self {
285 Self::Replace(models) | Self::Append(models) => models,
286 Self::NotSet => &[],
287 }
288 }
289
290 pub fn as_mut_vec(&mut self) -> &mut Vec<E::ActiveModelEx> {
292 match self {
293 Self::Replace(models) | Self::Append(models) => models,
294 Self::NotSet => {
295 *self = Self::Append(vec![]);
296 self.as_mut_vec()
297 }
298 }
299 }
300
301 pub fn into_vec(self) -> Vec<E::ActiveModelEx> {
303 match self {
304 Self::Replace(models) | Self::Append(models) => models,
305 Self::NotSet => vec![],
306 }
307 }
308
309 pub fn empty_holder(&self) -> Self {
311 match self {
312 Self::Replace(_) => Self::Replace(vec![]),
313 Self::Append(_) => Self::Append(vec![]),
314 Self::NotSet => Self::NotSet,
315 }
316 }
317
318 pub fn push<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
320 let model = model.into();
321 match self {
322 Self::Replace(models) | Self::Append(models) => models.push(model),
323 Self::NotSet => {
324 *self = Self::Append(vec![model]);
325 }
326 }
327
328 self
329 }
330
331 pub fn append<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
333 self.convert_to_append().push(model)
334 }
335
336 pub fn replace_all<I>(&mut self, models: I) -> &mut Self
338 where
339 I: IntoIterator<Item = E::ActiveModelEx>,
340 {
341 *self = Self::Replace(models.into_iter().collect());
342 self
343 }
344
345 pub fn convert_to_append(&mut self) -> &mut Self {
347 match self.take() {
348 Self::Replace(models) | Self::Append(models) => {
349 *self = Self::Append(models);
350 }
351 Self::NotSet => {
352 *self = Self::NotSet;
353 }
354 }
355
356 self
357 }
358
359 pub fn not_set(&mut self) {
361 *self = Self::NotSet;
362 }
363
364 pub fn is_replace(&self) -> bool {
366 matches!(self, Self::Replace(_))
367 }
368
369 pub fn is_append(&self) -> bool {
371 matches!(self, Self::Append(_))
372 }
373
374 pub fn is_changed(&self) -> bool {
376 match self {
377 Self::Replace(_) => true,
378 Self::Append(models) => models.iter().any(|model| model.is_changed()),
379 Self::NotSet => false,
380 }
381 }
382
383 pub fn find(&self, model: &E::Model) -> bool {
385 let pk = model.get_primary_key_value();
386
387 for item in self.as_slice() {
388 if let Some(pk_item) = item.get_primary_key_value()
389 && pk_item == pk
390 {
391 return true;
392 }
393 }
394
395 false
396 }
397
398 pub fn try_into_model(self) -> Result<HasMany<E>, DbErr>
400 where
401 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
402 {
403 Ok(match self {
404 Self::Replace(models) | Self::Append(models) => HasMany::Loaded(
405 models
406 .into_iter()
407 .map(|t| t.try_into_model())
408 .collect::<Result<Vec<_>, DbErr>>()?,
409 ),
410 Self::NotSet => HasMany::Unloaded,
411 })
412 }
413}
414
415impl<E: EntityTrait> From<ActiveHasMany<E>> for Option<Vec<E::ActiveModelEx>> {
416 fn from(value: ActiveHasMany<E>) -> Self {
417 match value {
418 ActiveHasMany::NotSet => None,
419 ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => Some(models),
420 }
421 }
422}
423
424impl<E: EntityTrait> Index<usize> for ActiveHasMany<E> {
425 type Output = E::ActiveModelEx;
426
427 fn index(&self, index: usize) -> &Self::Output {
428 match self {
429 ActiveHasMany::NotSet => {
430 panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
431 }
432 ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => models.index(index),
433 }
434 }
435}
436
437impl<E: EntityTrait> IndexMut<usize> for ActiveHasMany<E> {
438 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
439 match self {
440 ActiveHasMany::NotSet => {
441 panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
442 }
443 ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => {
444 models.index_mut(index)
445 }
446 }
447 }
448}
449
450impl<E: EntityTrait> IntoIterator for ActiveHasMany<E> {
451 type Item = E::ActiveModelEx;
452 type IntoIter = std::vec::IntoIter<E::ActiveModelEx>;
453
454 fn into_iter(self) -> Self::IntoIter {
455 match self {
456 ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => models.into_iter(),
457 ActiveHasMany::NotSet => Vec::new().into_iter(),
458 }
459 }
460}
461
462impl<E: EntityTrait> From<Vec<E::ActiveModelEx>> for ActiveHasMany<E> {
464 fn from(value: Vec<E::ActiveModelEx>) -> Self {
465 ActiveHasMany::Append(value)
466 }
467}