Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions sea-orm-macros/src/derives/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub mod value_type_attr {
pub from_str: Option<syn::LitStr>,
pub to_str: Option<syn::LitStr>,
pub try_from_u64: Option<()>,
pub no_vec_impl: Option<()>,
}
}

Expand Down
62 changes: 62 additions & 0 deletions sea-orm-macros/src/derives/value_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::derives::value_type_match::omit_vec_impl;

use super::attributes::value_type_attr;
use super::value_type_match::{array_type_expr, can_try_from_u64, column_type_expr};
use proc_macro2::TokenStream;
Expand All @@ -15,6 +17,8 @@ struct DeriveValueTypeStruct {
ty: Type,
column_type: TokenStream,
array_type: TokenStream,
/// Do not implement `sea_orm::TryGetableArray` for this type. Default: false.
no_vec_impl: bool,
can_try_from_u64: bool,
}

Expand All @@ -23,6 +27,7 @@ struct DeriveValueTypeStructAttrs {
column_type: Option<TokenStream>,
array_type: Option<TokenStream>,
try_from_u64: bool,
no_vec_impl: bool,
}

impl TryFrom<value_type_attr::SeaOrm> for DeriveValueTypeStructAttrs {
Expand All @@ -33,6 +38,7 @@ impl TryFrom<value_type_attr::SeaOrm> for DeriveValueTypeStructAttrs {
column_type: attrs.column_type.map(|s| s.parse()).transpose()?,
array_type: attrs.array_type.map(|s| s.parse()).transpose()?,
try_from_u64: attrs.try_from_u64.is_some(),
no_vec_impl: attrs.no_vec_impl.is_some(),
})
}
}
Expand Down Expand Up @@ -151,12 +157,14 @@ impl DeriveValueTypeStruct {
let column_type = column_type_expr(attrs.column_type, field_type, field_span);
let array_type = array_type_expr(attrs.array_type, field_type, field_span);
let can_try_from_u64 = attrs.try_from_u64 || can_try_from_u64(field_type);
let no_vec_impl = attrs.no_vec_impl || omit_vec_impl(field_type);

Ok(Self {
name,
ty,
column_type,
array_type,
no_vec_impl,
can_try_from_u64,
})
}
Expand Down Expand Up @@ -185,6 +193,25 @@ impl DeriveValueTypeStruct {
quote!()
};

let impl_try_getable_array = if cfg!(feature = "postgres-array") && !self.no_vec_impl {
quote!(
#[automatically_derived]
impl sea_orm::TryGetableArray for #name {
fn try_get_by<I: sea_orm::ColIdx>(
res: &sea_orm::QueryResult,
index: I,
) -> std::result::Result<Vec<Self>, sea_orm::TryGetError> {
Ok(<Vec<#field_type> as sea_orm::TryGetable>::try_get_by(res, index)?
.into_iter()
.map(|value| Self(value))
.collect())
}
}
)
} else {
quote!()
};

let impl_not_u8 = if cfg!(feature = "postgres-array") {
quote!(
#[automatically_derived]
Expand All @@ -198,6 +225,7 @@ impl DeriveValueTypeStruct {
#[automatically_derived]
impl std::convert::From<#name> for sea_orm::Value {
fn from(source: #name) -> Self {
println!("Struct");
source.0.into()
}
}
Expand Down Expand Up @@ -243,6 +271,8 @@ impl DeriveValueTypeStruct {
}
}

#impl_try_getable_array

#try_from_u64_impl

#impl_not_u8
Expand Down Expand Up @@ -275,6 +305,36 @@ impl DeriveValueTypeString {
None => &quote!(String(sea_orm::sea_query::StringLen::None)),
};

let impl_try_getable_array = if cfg!(feature = "postgres-array") {
quote!(
#[automatically_derived]
impl sea_orm::TryGetableArray for #name {
fn try_get_by<I: sea_orm::ColIdx>(
res: &sea_orm::QueryResult,
index: I,
) -> std::result::Result<Vec<Self>, sea_orm::TryGetError> {
let mut result = Vec::new();
for string in <Vec<String> as sea_orm::TryGetable>::try_get_by(res, index)?.into_iter() {
result.push(#from_str(&string)
.map_err(|err|
{
sea_orm::TryGetError::DbErr(
sea_orm::DbErr::TryIntoErr {
from: "String",
into: stringify!(#name),
source: std::sync::Arc::new(err),
})
}
)?);
}
Ok(result)
}
}
)
} else {
quote!()
};

let impl_not_u8 = if cfg!(feature = "postgres-array") {
quote!(
#[automatically_derived]
Expand Down Expand Up @@ -342,6 +402,8 @@ impl DeriveValueTypeString {
}

#impl_not_u8

#impl_try_getable_array
)
}
}
Expand Down
23 changes: 23 additions & 0 deletions sea-orm-macros/src/derives/value_type_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,29 @@ pub fn can_try_from_u64(field_type: &str) -> bool {
)
}

/// Maximum depth of vector nesting allowed INSIDE NEW TYPE before omitting sea_orm::TryGetableArray
/// For example, `struct A (Vec<Vec<i32>>)` has dimensionality of 2
/// Abosolute maximum would be 5, because of Postgres limit of 6
const MAX_VEC_DIMENSIONALITY: u8 = 0;

/// Determines whether to omit `sea_orm::TryGetableArray` implementation for a given field type
/// based on the vector dimensionality.
pub fn omit_vec_impl(field_type: &str) -> bool {
let mut depth = 0u8;
let mut current = field_type.trim();

while let Some(inner) = current.strip_prefix("Vec<") {
#[allow(clippy::absurd_extreme_comparisons)]
if depth >= MAX_VEC_DIMENSIONALITY {
return true;
}
depth += 1;
current = inner.trim_start();
}

false
}

/// Return whether it is nullable
fn trim_option(s: &str) -> (bool, &str) {
if s.starts_with("Option<") {
Expand Down
6 changes: 6 additions & 0 deletions sea-orm-sync/tests/common/features/value_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,15 @@ where
}
}

// Automatically disable vec impl
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
pub struct StringVec(pub Vec<String>);

// Explicitly disable vec impl
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(no_vec_impl)]
pub struct StringVecNoImpl(pub Vec<String>);

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub enum Tag1 {
Expand Down
66 changes: 66 additions & 0 deletions sea-orm-sync/tests/derive_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,69 @@ struct FromQueryResultNested {
#[sea_orm(nested)]
_test: SimpleTest,
}

#[cfg(feature = "postgres-array")]
mod postgres_array {
use crate::FromQueryResult;
use sea_orm::DeriveValueType;

#[derive(DeriveValueType)]
pub struct IngredientId(i32);

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub struct NumericLabel {
pub value: i64,
}

impl std::fmt::Display for NumericLabel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

impl std::str::FromStr for NumericLabel {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self { value: s.parse()? })
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub enum TextureKind {
Hard,
Soft,
}

impl std::fmt::Display for TextureKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Hard => "hard",
Self::Soft => "soft",
}
)
}
}

impl std::str::FromStr for TextureKind {
type Err = sea_query::ValueTypeErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"hard" => Self::Hard,
"soft" => Self::Soft,
_ => return Err(sea_query::ValueTypeErr),
})
}
}

#[derive(FromQueryResult)]
pub struct IngredientPathRow {
pub ingredient_path: Vec<IngredientId>,
pub numeric_label_path: Vec<NumericLabel>,
pub texture_path: Vec<TextureKind>,
}
}
6 changes: 6 additions & 0 deletions tests/common/features/value_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,15 @@ where
}
}

// Automatically disable vec impl
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
pub struct StringVec(pub Vec<String>);

// Explicitly disable vec impl
#[derive(Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(no_vec_impl)]
pub struct StringVecNoImpl(pub Vec<String>);

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub enum Tag1 {
Expand Down
66 changes: 66 additions & 0 deletions tests/derive_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,69 @@ struct FromQueryResultNested {
#[sea_orm(nested)]
_test: SimpleTest,
}

#[cfg(feature = "postgres-array")]
mod postgres_array {
use crate::FromQueryResult;
use sea_orm::DeriveValueType;

#[derive(DeriveValueType)]
pub struct IngredientId(i32);

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub struct NumericLabel {
pub value: i64,
}

impl std::fmt::Display for NumericLabel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}

impl std::str::FromStr for NumericLabel {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self { value: s.parse()? })
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, DeriveValueType)]
#[sea_orm(value_type = "String")]
pub enum TextureKind {
Hard,
Soft,
}

impl std::fmt::Display for TextureKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Hard => "hard",
Self::Soft => "soft",
}
)
}
}

impl std::str::FromStr for TextureKind {
type Err = sea_query::ValueTypeErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"hard" => Self::Hard,
"soft" => Self::Soft,
_ => return Err(sea_query::ValueTypeErr),
})
}
}

#[derive(FromQueryResult)]
pub struct IngredientPathRow {
pub ingredient_path: Vec<IngredientId>,
pub numeric_label_path: Vec<NumericLabel>,
pub texture_path: Vec<TextureKind>,
}
}
Loading