1use super::compound::{HasMany, HasOne};
2use crate::{ActiveModelTrait, DbErr, EntityTrait, ModelTrait, TryIntoModel};
3use core::ops::{Index, IndexMut};
4
5#[derive(Debug, Default, Clone)]
10pub enum HasOneModel<E: EntityTrait> {
11 #[default]
13 NotSet,
14 Set(Box<E::ActiveModelEx>),
16}
17
18#[derive(Debug, Default, Clone)]
22pub enum HasManyModel<E: EntityTrait> {
23 #[default]
25 NotSet,
26 Replace(Vec<E::ActiveModelEx>),
29 Append(Vec<E::ActiveModelEx>),
32}
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
38pub enum ActiveModelAction {
39 Insert,
41 Update,
43 Save,
46}
47
48impl<E> HasOneModel<E>
49where
50 E: EntityTrait,
51{
52 pub fn set<AM: Into<E::ActiveModelEx>>(model: AM) -> Self {
54 Self::Set(Box::new(model.into()))
55 }
56
57 pub fn replace<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) {
59 *self = Self::Set(Box::new(model.into()));
60 }
61
62 pub fn take(&mut self) -> Option<E::ActiveModelEx> {
64 match std::mem::take(self) {
65 Self::Set(model) => Some(*model),
66 _ => None,
67 }
68 }
69
70 pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
72 match self {
73 Self::Set(model) => Some(model),
74 _ => None,
75 }
76 }
77
78 #[allow(clippy::should_implement_trait)]
80 pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
81 match self {
82 Self::Set(model) => Some(model),
83 _ => None,
84 }
85 }
86
87 pub fn is_set(&self) -> bool {
89 matches!(self, Self::Set(_))
90 }
91
92 pub fn is_not_set(&self) -> bool {
94 matches!(self, Self::NotSet)
95 }
96
97 pub fn is_none(&self) -> bool {
99 matches!(self, Self::NotSet)
100 }
101
102 pub fn is_changed(&self) -> bool {
104 match self {
105 Self::Set(model) => model.is_changed(),
106 _ => false,
107 }
108 }
109
110 pub fn into_option(self) -> Option<E::ActiveModelEx> {
112 match self {
113 Self::Set(model) => Some(*model),
114 Self::NotSet => None,
115 }
116 }
117
118 #[doc(hidden)]
120 pub fn empty_slice(&self) -> &[E::ActiveModelEx] {
121 &[]
122 }
123
124 pub fn try_into_model(self) -> Result<HasOne<E>, DbErr>
126 where
127 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
128 {
129 Ok(match self {
130 Self::Set(model) => HasOne::Loaded(Box::new((*model).try_into_model()?)),
131 Self::NotSet => HasOne::Unloaded,
132 })
133 }
134}
135
136impl<E> PartialEq for HasOneModel<E>
137where
138 E: EntityTrait,
139 E::ActiveModelEx: PartialEq,
140{
141 fn eq(&self, other: &Self) -> bool {
142 match (self, other) {
143 (HasOneModel::NotSet, HasOneModel::NotSet) => true,
144 (HasOneModel::Set(a), HasOneModel::Set(b)) => a == b,
145 _ => false,
146 }
147 }
148}
149
150impl<E> PartialEq<Option<E::ActiveModelEx>> for HasOneModel<E>
151where
152 E: EntityTrait,
153 E::ActiveModelEx: PartialEq,
154{
155 fn eq(&self, other: &Option<E::ActiveModelEx>) -> bool {
156 match (self, other) {
157 (HasOneModel::NotSet, None) => true,
158 (HasOneModel::Set(a), Some(b)) => a.as_ref() == b,
159 _ => false,
160 }
161 }
162}
163
164impl<E> Eq for HasOneModel<E>
165where
166 E: EntityTrait,
167 E::ActiveModelEx: Eq,
168{
169}
170
171impl<E> HasManyModel<E>
172where
173 E: EntityTrait,
174{
175 pub fn take(&mut self) -> Self {
177 std::mem::take(self)
178 }
179
180 pub fn as_slice(&self) -> &[E::ActiveModelEx] {
182 match self {
183 Self::Replace(models) | Self::Append(models) => models,
184 Self::NotSet => &[],
185 }
186 }
187
188 pub fn as_mut_vec(&mut self) -> &mut Vec<E::ActiveModelEx> {
190 match self {
191 Self::Replace(models) | Self::Append(models) => models,
192 Self::NotSet => {
193 *self = Self::Append(vec![]);
194 self.as_mut_vec()
195 }
196 }
197 }
198
199 pub fn into_vec(self) -> Vec<E::ActiveModelEx> {
201 match self {
202 Self::Replace(models) | Self::Append(models) => models,
203 Self::NotSet => vec![],
204 }
205 }
206
207 pub fn empty_holder(&self) -> Self {
209 match self {
210 Self::Replace(_) => Self::Replace(vec![]),
211 Self::Append(_) => Self::Append(vec![]),
212 Self::NotSet => Self::NotSet,
213 }
214 }
215
216 pub fn push<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
218 let model = model.into();
219 match self {
220 Self::Replace(models) | Self::Append(models) => models.push(model),
221 Self::NotSet => {
222 *self = Self::Append(vec![model]);
223 }
224 }
225
226 self
227 }
228
229 pub fn append<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
231 self.convert_to_append().push(model)
232 }
233
234 pub fn replace_all<I>(&mut self, models: I) -> &mut Self
236 where
237 I: IntoIterator<Item = E::ActiveModelEx>,
238 {
239 *self = Self::Replace(models.into_iter().collect());
240 self
241 }
242
243 pub fn convert_to_append(&mut self) -> &mut Self {
245 match self.take() {
246 Self::Replace(models) | Self::Append(models) => {
247 *self = Self::Append(models);
248 }
249 Self::NotSet => {
250 *self = Self::NotSet;
251 }
252 }
253
254 self
255 }
256
257 pub fn not_set(&mut self) {
259 *self = Self::NotSet;
260 }
261
262 pub fn is_replace(&self) -> bool {
264 matches!(self, Self::Replace(_))
265 }
266
267 pub fn is_append(&self) -> bool {
269 matches!(self, Self::Append(_))
270 }
271
272 pub fn is_changed(&self) -> bool {
274 match self {
275 Self::Replace(_) => true,
276 Self::Append(models) => models.iter().any(|model| model.is_changed()),
277 Self::NotSet => false,
278 }
279 }
280
281 pub fn find(&self, model: &E::Model) -> bool {
283 let pk = model.get_primary_key_value();
284
285 for item in self.as_slice() {
286 if let Some(pk_item) = item.get_primary_key_value()
287 && pk_item == pk
288 {
289 return true;
290 }
291 }
292
293 false
294 }
295
296 pub fn try_into_model(self) -> Result<HasMany<E>, DbErr>
298 where
299 E::ActiveModelEx: TryIntoModel<E::ModelEx>,
300 {
301 Ok(match self {
302 Self::Replace(models) | Self::Append(models) => HasMany::Loaded(
303 models
304 .into_iter()
305 .map(|t| t.try_into_model())
306 .collect::<Result<Vec<_>, DbErr>>()?,
307 ),
308 Self::NotSet => HasMany::Unloaded,
309 })
310 }
311}
312
313impl<E> PartialEq for HasManyModel<E>
314where
315 E: EntityTrait,
316 E::ActiveModelEx: PartialEq,
317{
318 fn eq(&self, other: &Self) -> bool {
319 match (self, other) {
320 (HasManyModel::NotSet, HasManyModel::NotSet) => true,
321 (HasManyModel::Replace(a), HasManyModel::Replace(b)) => a == b,
322 (HasManyModel::Append(a), HasManyModel::Append(b)) => a == b,
323 _ => false,
324 }
325 }
326}
327
328impl<E> Eq for HasManyModel<E>
329where
330 E: EntityTrait,
331 E::ActiveModelEx: Eq,
332{
333}
334
335impl<E: EntityTrait> From<HasManyModel<E>> for Option<Vec<E::ActiveModelEx>> {
336 fn from(value: HasManyModel<E>) -> Self {
337 match value {
338 HasManyModel::NotSet => None,
339 HasManyModel::Replace(models) | HasManyModel::Append(models) => Some(models),
340 }
341 }
342}
343
344impl<E: EntityTrait> Index<usize> for HasManyModel<E> {
345 type Output = E::ActiveModelEx;
346
347 fn index(&self, index: usize) -> &Self::Output {
348 match self {
349 HasManyModel::NotSet => {
350 panic!("index out of bounds: the HasManyModel is NotSet (index: {index})")
351 }
352 HasManyModel::Replace(models) | HasManyModel::Append(models) => models.index(index),
353 }
354 }
355}
356
357impl<E: EntityTrait> IndexMut<usize> for HasManyModel<E> {
358 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
359 match self {
360 HasManyModel::NotSet => {
361 panic!("index out of bounds: the HasManyModel is NotSet (index: {index})")
362 }
363 HasManyModel::Replace(models) | HasManyModel::Append(models) => models.index_mut(index),
364 }
365 }
366}
367
368impl<E: EntityTrait> IntoIterator for HasManyModel<E> {
369 type Item = E::ActiveModelEx;
370 type IntoIter = std::vec::IntoIter<E::ActiveModelEx>;
371
372 fn into_iter(self) -> Self::IntoIter {
373 match self {
374 HasManyModel::Replace(models) | HasManyModel::Append(models) => models.into_iter(),
375 HasManyModel::NotSet => Vec::new().into_iter(),
376 }
377 }
378}
379
380impl<E: EntityTrait> From<Vec<E::ActiveModelEx>> for HasManyModel<E> {
382 fn from(value: Vec<E::ActiveModelEx>) -> Self {
383 HasManyModel::Append(value)
384 }
385}