Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework ActiveEnum to support TryGetable for Vec<Json> #1898

Merged
merged 4 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions sea-orm-macros/src/derives/active_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ impl ActiveEnum {
}
}

#[automatically_derived]
impl sea_orm::TryGetableArray for #ident {
fn try_get_by<I: sea_orm::ColIdx>(res: &sea_orm::QueryResult, index: I) -> std::result::Result<Vec<Self>, sea_orm::TryGetError> {
<<Self as sea_orm::ActiveEnum>::Value as sea_orm::ActiveEnumValue>::try_get_vec_by(res, index)?
.into_iter()
.map(|value| Self::try_from_value(&value).map_err(Into::into))
tyt2y3 marked this conversation as resolved.
Show resolved Hide resolved
.collect()
}
}

#[automatically_derived]
#[allow(clippy::from_over_into)]
impl Into<sea_orm::sea_query::Value> for #ident {
Expand Down
66 changes: 50 additions & 16 deletions src/entity/active_enum.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ColumnDef, DbErr, Iterable, QueryResult, TryFromU64, TryGetError, TryGetable};
use crate::{ColIdx, ColumnDef, DbErr, Iterable, QueryResult, TryFromU64, TryGetError, TryGetable};
use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType};

/// A Rust representation of enum defined in database.
Expand Down Expand Up @@ -110,11 +110,11 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType};
/// impl ActiveModelBehavior for ActiveModel {}
/// ```
pub trait ActiveEnum: Sized + Iterable {
/// Define the Rust type that each enum variant represents.
type Value: Into<Value> + ValueType + Nullable + TryGetable;
/// Define the Rust type that each enum variant corresponds.
type Value: ActiveEnumValue;

/// Define the enum value in Vector type.
type ValueVec: IntoIterator<Item = Self::Value>;
/// This has no purpose. It will be removed in the next major version.
type ValueVec;

/// Get the name of enum
fn name() -> DynIden;
Expand Down Expand Up @@ -144,19 +144,53 @@ pub trait ActiveEnum: Sized + Iterable {
}
}

impl<T> TryGetable for Vec<T>
where
T: ActiveEnum,
T::ValueVec: TryGetable,
{
fn try_get_by<I: crate::ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
<T::ValueVec as TryGetable>::try_get_by(res, index)?
.into_iter()
.map(|value| T::try_from_value(&value).map_err(Into::into))
.collect()
}
/// The Rust Value backing ActiveEnums
pub trait ActiveEnumValue: Into<Value> + ValueType + Nullable + TryGetable {
/// For getting an array of enum. Postgres only
fn try_get_vec_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
}

macro_rules! impl_active_enum_value {
($type:ident) => {
impl ActiveEnumValue for $type {
fn try_get_vec_by<I: ColIdx>(
_res: &QueryResult,
_index: I,
) -> Result<Vec<Self>, TryGetError> {
panic!("Not supported by `postgres-array`")
}
}
};
}

macro_rules! impl_active_enum_value_with_pg_array {
($type:ident) => {
impl ActiveEnumValue for $type {
fn try_get_vec_by<I: ColIdx>(
_res: &QueryResult,
_index: I,
) -> Result<Vec<Self>, TryGetError> {
#[cfg(feature = "postgres-array")]
{
<Vec<Self>>::try_get_by(_res, _index)
}
#[cfg(not(feature = "postgres-array"))]
panic!("`postgres-array` is not enabled")
}
}
};
}

impl_active_enum_value!(u8);
impl_active_enum_value!(u16);
impl_active_enum_value!(u32);
impl_active_enum_value!(u64);
impl_active_enum_value_with_pg_array!(String);
impl_active_enum_value_with_pg_array!(i8);
impl_active_enum_value_with_pg_array!(i16);
impl_active_enum_value_with_pg_array!(i32);
impl_active_enum_value_with_pg_array!(i64);

impl<T> TryFromU64 for T
where
T: ActiveEnum,
Expand Down
45 changes: 45 additions & 0 deletions src/executor/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,25 @@ fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), Try
}
}

/// An interface to get an array of values from the query result.
/// A type can only implement `ActiveEnum` or `TryGetableFromJson`, but not both.
/// A blanket impl is provided for `TryGetableFromJson`, while the impl for `ActiveEnum`
/// is provided by the `DeriveActiveEnum` macro. So as an end user you won't normally
/// touch this trait.
pub trait TryGetableArray: Sized {
/// Just a delegate
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<Self>, TryGetError>;
}

impl<T> TryGetable for Vec<T>
where
T: TryGetableArray,
{
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
T::try_get_by(res, index)
}
}

// TryGetableFromJson //

/// An interface to get a JSON from the query result
Expand Down Expand Up @@ -999,6 +1018,22 @@ where
_ => unreachable!(),
}
}

/// Get a Vec<Self> from an Array of Json
fn from_json_vec(value: serde_json::Value) -> Result<Vec<Self>, TryGetError> {
match value {
serde_json::Value::Array(values) => {
let mut res = Vec::new();
for item in values {
res.push(serde_json::from_value(item).map_err(json_err)?);
}
Ok(res)
}
_ => Err(TryGetError::DbErr(DbErr::Json(
"Value is not an Array".to_owned(),
))),
}
}
}

#[cfg(feature = "with-json")]
Expand All @@ -1011,6 +1046,16 @@ where
}
}

#[cfg(feature = "with-json")]
impl<T> TryGetableArray for T
where
T: TryGetableFromJson,
{
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Vec<T>, TryGetError> {
T::from_json_vec(serde_json::Value::try_get_by(res, index)?)
}
}

// TryFromU64 //
/// Try to convert a type to a u64
pub trait TryFromU64: Sized {
Expand Down