use super::compound::{BelongsTo, BelongsToCardinality, HasMany, HasOne};
use crate::{ActiveModelTrait, DbErr, EntityTrait, ModelTrait, TryIntoModel};
use core::ops::{Index, IndexMut};
#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; <T as BelongsToCardinality>::Set)]
#[derive(Default)]
pub enum ActiveBelongsTo<T>
where
T: BelongsToCardinality,
{
#[default]
NotSet,
Set(<T as BelongsToCardinality>::Set),
}
#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; E::ActiveModelEx)]
#[derive(Default)]
pub enum ActiveHasOne<E>
where
E: EntityTrait,
{
#[default]
NotSet,
Set(Option<Box<E::ActiveModelEx>>),
}
#[derive_where::derive_where(Debug, Clone, PartialEq, Eq; E::ActiveModelEx)]
#[derive(Default)]
#[non_exhaustive]
pub enum ActiveHasMany<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<T> ActiveBelongsTo<T>
where
T: BelongsToCardinality,
{
pub fn set<M>(model: T::Value<M>) -> Self
where
M: Into<<<T as BelongsToCardinality>::Entity as EntityTrait>::ActiveModelEx>,
{
Self::Set(T::into_set(model))
}
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub fn is_not_set(&self) -> bool {
matches!(self, Self::NotSet)
}
pub fn is_set(&self) -> bool {
matches!(self, Self::Set(_))
}
}
impl<E> ActiveBelongsTo<E>
where
E: EntityTrait,
{
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_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,
}
}
pub fn try_into_model(self) -> Result<BelongsTo<E>, DbErr>
where
E::ActiveModelEx: TryIntoModel<E::ModelEx>,
{
Ok(match self {
Self::Set(model) => BelongsTo::Loaded(Box::new((*model).try_into_model()?)),
Self::NotSet => BelongsTo::Unloaded,
})
}
}
impl<E> ActiveBelongsTo<Option<E>>
where
E: EntityTrait,
{
pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(model),
_ => None,
}
}
#[allow(clippy::should_implement_trait)]
pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(model),
_ => None,
}
}
pub fn is_changed(&self) -> bool {
match self {
Self::Set(Some(model)) => model.is_changed(),
Self::Set(None) => true,
Self::NotSet => false,
}
}
pub fn into_option(self) -> Option<E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(*model),
Self::Set(None) | Self::NotSet => None,
}
}
pub fn try_into_model(self) -> Result<BelongsTo<Option<E>>, DbErr>
where
E::ActiveModelEx: TryIntoModel<E::ModelEx>,
{
Ok(match self {
Self::Set(Some(model)) => BelongsTo::Loaded(Some(Box::new((*model).try_into_model()?))),
Self::Set(None) => BelongsTo::Loaded(None),
Self::NotSet => BelongsTo::Unloaded,
})
}
}
impl<E> ActiveHasOne<E>
where
E: EntityTrait,
{
pub fn set(model: Option<impl Into<E::ActiveModelEx>>) -> Self {
Self::Set(model.map(|model| Box::new(model.into())))
}
pub fn is_not_set(&self) -> bool {
matches!(self, Self::NotSet)
}
pub fn is_set(&self) -> bool {
matches!(self, Self::Set(_))
}
pub fn as_ref(&self) -> Option<&E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(model),
_ => None,
}
}
#[allow(clippy::should_implement_trait)]
pub fn as_mut(&mut self) -> Option<&mut E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(model),
_ => None,
}
}
pub fn is_changed(&self) -> bool {
match self {
Self::Set(Some(model)) => model.is_changed(),
Self::Set(None) => true,
Self::NotSet => false,
}
}
pub fn into_option(self) -> Option<E::ActiveModelEx> {
match self {
Self::Set(Some(model)) => Some(*model),
Self::Set(None) | Self::NotSet => None,
}
}
pub fn try_into_model(self) -> Result<HasOne<E>, DbErr>
where
E::ActiveModelEx: TryIntoModel<E::ModelEx>,
{
Ok(match self {
Self::Set(Some(model)) => HasOne::Loaded(Some(Box::new((*model).try_into_model()?))),
Self::Set(None) => HasOne::Loaded(None),
Self::NotSet => HasOne::Unloaded,
})
}
}
impl<E> ActiveHasMany<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()
&& 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: EntityTrait> From<ActiveHasMany<E>> for Option<Vec<E::ActiveModelEx>> {
fn from(value: ActiveHasMany<E>) -> Self {
match value {
ActiveHasMany::NotSet => None,
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => Some(models),
}
}
}
impl<E: EntityTrait> Index<usize> for ActiveHasMany<E> {
type Output = E::ActiveModelEx;
fn index(&self, index: usize) -> &Self::Output {
match self {
ActiveHasMany::NotSet => {
panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
}
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => models.index(index),
}
}
}
impl<E: EntityTrait> IndexMut<usize> for ActiveHasMany<E> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match self {
ActiveHasMany::NotSet => {
panic!("index out of bounds: the ActiveHasMany is NotSet (index: {index})")
}
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => {
models.index_mut(index)
}
}
}
}
impl<E: EntityTrait> IntoIterator for ActiveHasMany<E> {
type Item = E::ActiveModelEx;
type IntoIter = std::vec::IntoIter<E::ActiveModelEx>;
fn into_iter(self) -> Self::IntoIter {
match self {
ActiveHasMany::Replace(models) | ActiveHasMany::Append(models) => models.into_iter(),
ActiveHasMany::NotSet => Vec::new().into_iter(),
}
}
}
impl<E: EntityTrait> From<Vec<E::ActiveModelEx>> for ActiveHasMany<E> {
fn from(value: Vec<E::ActiveModelEx>) -> Self {
ActiveHasMany::Append(value)
}
}