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