use super::compound::{HasMany, HasOne};
use crate::{ActiveModelTrait, DbErr, EntityTrait, ModelTrait, TryIntoModel};
use core::ops::{Index, IndexMut};
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub enum ActiveHasOne<E: EntityTrait> {
#[default]
NotSet,
Set(Box<E::ActiveModelEx>),
Delete,
}
#[derive(Debug, Default, Clone)]
#[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<E> ActiveHasOne<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 is_delete(&self) -> bool {
matches!(self, Self::Delete)
}
#[doc(hidden)]
pub fn as_slice(&self) -> &[E::ActiveModelEx] {
match self {
Self::Set(model) => std::slice::from_ref(model.as_ref()),
_ => &[],
}
}
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 into_option(self) -> Option<E::ActiveModelEx> {
match self {
Self::Set(model) => Some(*model),
Self::NotSet | Self::Delete => 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 | Self::Delete => HasOne::Unloaded,
})
}
}
impl<E> PartialEq for ActiveHasOne<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ActiveHasOne::NotSet, ActiveHasOne::NotSet) => true,
(ActiveHasOne::Set(a), ActiveHasOne::Set(b)) => a == b,
(ActiveHasOne::Delete, ActiveHasOne::Delete) => true,
_ => false,
}
}
}
impl<E> PartialEq<Option<E::ActiveModelEx>> for ActiveHasOne<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Option<E::ActiveModelEx>) -> bool {
match (self, other) {
(ActiveHasOne::NotSet, None) => true,
(ActiveHasOne::Set(a), Some(b)) => a.as_ref() == b,
_ => false,
}
}
}
impl<E> Eq for ActiveHasOne<E>
where
E: EntityTrait,
E::ActiveModelEx: Eq,
{
}
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> PartialEq for ActiveHasMany<E>
where
E: EntityTrait,
E::ActiveModelEx: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ActiveHasMany::NotSet, ActiveHasMany::NotSet) => true,
(ActiveHasMany::Replace(a), ActiveHasMany::Replace(b)) => a == b,
(ActiveHasMany::Append(a), ActiveHasMany::Append(b)) => a == b,
_ => false,
}
}
}
impl<E> Eq for ActiveHasMany<E>
where
E: EntityTrait,
E::ActiveModelEx: Eq,
{
}
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)
}
}