use super::compound::{HasMany, HasOne};
use crate::{ActiveModelTrait, DbErr, EntityTrait, ModelTrait, TryIntoModel};
use core::ops::{Index, IndexMut};
#[derive(Debug, Default, Clone)]
pub enum HasOneModel<E: EntityTrait> {
#[default]
NotSet,
Set(Box<E::ActiveModelEx>),
}
#[derive(Debug, Default, Clone)]
pub enum HasManyModel<E: EntityTrait> {
#[default]
NotSet,
Replace(Vec<E::ActiveModelEx>),
Append(Vec<E::ActiveModelEx>),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ActiveModelAction {
Insert,
Update,
Save,
}
impl<E> HasOneModel<E>
where
E: EntityTrait,
{
pub fn set<AM: Into<E::ActiveModelEx>>(model: AM) -> Self {
Self::Set(Box::new(model.into()))
}
pub fn replace<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) {
*self = Self::Set(Box::new(model.into()));
}
pub fn take(&mut self) -> Option<E::ActiveModelEx> {
match std::mem::take(self) {
Self::Set(model) => Some(*model),
_ => None,
}
}
pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
match self {
Self::Set(model) => Some(model),
_ => None,
}
}
#[allow(clippy::should_implement_trait)]
pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
match self {
Self::Set(model) => Some(model),
_ => None,
}
}
pub fn is_set(&self) -> bool {
matches!(self, Self::Set(_))
}
pub fn is_not_set(&self) -> bool {
matches!(self, Self::NotSet)
}
pub fn is_none(&self) -> bool {
matches!(self, Self::NotSet)
}
pub fn is_changed(&self) -> bool {
match self {
Self::Set(model) => model.is_changed(),
_ => false,
}
}
pub fn into_option(self) -> Option<E::ActiveModelEx> {
match self {
Self::Set(model) => Some(*model),
Self::NotSet => None,
}
}
#[doc(hidden)]
pub fn empty_slice(&self) -> &[E::ActiveModelEx] {
&[]
}
pub fn try_into_model(self) -> Result<HasOne<E>, DbErr>
where
E::ActiveModelEx: TryIntoModel<E::ModelEx>,
{
Ok(match self {
Self::Set(model) => HasOne::Loaded(Box::new((*model).try_into_model()?)),
Self::NotSet => HasOne::Unloaded,
})
}
}
impl<E> PartialEq for HasOneModel<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(HasOneModel::NotSet, HasOneModel::NotSet) => true,
(HasOneModel::Set(a), HasOneModel::Set(b)) => a == b,
_ => false,
}
}
}
impl<E> PartialEq<Option<E::ActiveModelEx>> for HasOneModel<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Option<E::ActiveModelEx>) -> bool {
match (self, other) {
(HasOneModel::NotSet, None) => true,
(HasOneModel::Set(a), Some(b)) => a.as_ref() == b,
_ => false,
}
}
}
impl<E> Eq for HasOneModel<E>
where
E: EntityTrait,
E::ActiveModelEx: Eq,
{
}
impl<E> HasManyModel<E>
where
E: EntityTrait,
{
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub fn as_slice(&self) -> &[E::ActiveModelEx] {
match self {
Self::Replace(models) | Self::Append(models) => models,
Self::NotSet => &[],
}
}
pub fn as_mut_vec(&mut self) -> &mut Vec<E::ActiveModelEx> {
match self {
Self::Replace(models) | Self::Append(models) => models,
Self::NotSet => {
*self = Self::Append(vec![]);
self.as_mut_vec()
}
}
}
pub fn into_vec(self) -> Vec<E::ActiveModelEx> {
match self {
Self::Replace(models) | Self::Append(models) => models,
Self::NotSet => vec![],
}
}
pub fn empty_holder(&self) -> Self {
match self {
Self::Replace(_) => Self::Replace(vec![]),
Self::Append(_) => Self::Append(vec![]),
Self::NotSet => Self::NotSet,
}
}
pub fn push<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
let model = model.into();
match self {
Self::Replace(models) | Self::Append(models) => models.push(model),
Self::NotSet => {
*self = Self::Append(vec![model]);
}
}
self
}
pub fn append<AM: Into<E::ActiveModelEx>>(&mut self, model: AM) -> &mut Self {
self.convert_to_append().push(model)
}
pub fn replace_all<I>(&mut self, models: I) -> &mut Self
where
I: IntoIterator<Item = E::ActiveModelEx>,
{
*self = Self::Replace(models.into_iter().collect());
self
}
pub fn convert_to_append(&mut self) -> &mut Self {
match self.take() {
Self::Replace(models) | Self::Append(models) => {
*self = Self::Append(models);
}
Self::NotSet => {
*self = Self::NotSet;
}
}
self
}
pub fn not_set(&mut self) {
*self = Self::NotSet;
}
pub fn is_replace(&self) -> bool {
matches!(self, Self::Replace(_))
}
pub fn is_append(&self) -> bool {
matches!(self, Self::Append(_))
}
pub fn is_changed(&self) -> bool {
match self {
Self::Replace(_) => true,
Self::Append(models) => models.iter().any(|model| model.is_changed()),
Self::NotSet => false,
}
}
pub fn find(&self, model: &E::Model) -> bool {
let pk = model.get_primary_key_value();
for item in self.as_slice() {
if let Some(pk_item) = item.get_primary_key_value() {
if pk_item == pk {
return true;
}
}
}
false
}
pub fn try_into_model(self) -> Result<HasMany<E>, DbErr>
where
E::ActiveModelEx: TryIntoModel<E::ModelEx>,
{
Ok(match self {
Self::Replace(models) | Self::Append(models) => HasMany::Loaded(
models
.into_iter()
.map(|t| t.try_into_model())
.collect::<Result<Vec<_>, DbErr>>()?,
),
Self::NotSet => HasMany::Unloaded,
})
}
}
impl<E> PartialEq for HasManyModel<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(HasManyModel::NotSet, HasManyModel::NotSet) => true,
(HasManyModel::Replace(a), HasManyModel::Replace(b)) => a == b,
(HasManyModel::Append(a), HasManyModel::Append(b)) => a == b,
_ => false,
}
}
}
impl<E> Eq for HasManyModel<E>
where
E: EntityTrait,
E::ActiveModelEx: Eq,
{
}
impl<E: EntityTrait> From<HasManyModel<E>> for Option<Vec<E::ActiveModelEx>> {
fn from(value: HasManyModel<E>) -> Self {
match value {
HasManyModel::NotSet => None,
HasManyModel::Replace(models) | HasManyModel::Append(models) => Some(models),
}
}
}
impl<E: EntityTrait> Index<usize> for HasManyModel<E> {
type Output = E::ActiveModelEx;
fn index(&self, index: usize) -> &Self::Output {
match self {
HasManyModel::NotSet => {
panic!("index out of bounds: the HasManyModel is NotSet (index: {index})")
}
HasManyModel::Replace(models) | HasManyModel::Append(models) => models.index(index),
}
}
}
impl<E: EntityTrait> IndexMut<usize> for HasManyModel<E> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match self {
HasManyModel::NotSet => {
panic!("index out of bounds: the HasManyModel is NotSet (index: {index})")
}
HasManyModel::Replace(models) | HasManyModel::Append(models) => models.index_mut(index),
}
}
}
impl<E: EntityTrait> IntoIterator for HasManyModel<E> {
type Item = E::ActiveModelEx;
type IntoIter = std::vec::IntoIter<E::ActiveModelEx>;
fn into_iter(self) -> Self::IntoIter {
match self {
HasManyModel::Replace(models) | HasManyModel::Append(models) => models.into_iter(),
HasManyModel::NotSet => Vec::new().into_iter(),
}
}
}
impl<E: EntityTrait> From<Vec<E::ActiveModelEx>> for HasManyModel<E> {
fn from(value: Vec<E::ActiveModelEx>) -> Self {
HasManyModel::Append(value)
}
}