SeaQL / sea-orm

🐚 An async & dynamic ORM for Rust
https://www.sea-ql.org/SeaORM/
Apache License 2.0
6.94k stars 483 forks source link

FromJsonQueryResult should also work with generic types #2049

Open nitn3lav opened 8 months ago

nitn3lav commented 8 months ago

Description

FromJsonQueryResult currently doesn't work with generics. This is useful to e. g. wrap a Vec<T> in a custom type List<T>(Vec<T>) that implements the required traits to be used in SeaORM.

This is caused by the FromJsonQueryResult currently ignoring any generics. This can be fixed with these changes to expand_derive_from_json_query_result and derive_from_json_query_result:

Steps to Reproduce

  1. cargo new test-project
  2. cd test-project
  3. cargo add sea-orm
  4. cargo add serde -F derive
  5. cargo add serde_json
  6. Paste this code into src/main.rs
    
    use sea_orm::{prelude::*, FromJsonQueryResult};
    use serde::{Deserialize, Serialize};

[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, FromJsonQueryResult)]

[serde(transparent)]

pub struct List(pub Vec);


### Expected Behavior

compiles without errors

### Actual Behavior

```console
error[E0107]: missing generics for struct `List`
  --> src/main.rs:16:12
   |
16 | pub struct List<T>(pub Vec<T>);
   |            ^^^^ expected 1 generic argument
   |
note: struct defined here, with 1 generic parameter: `T`
  --> src/main.rs:16:12
   |
16 | pub struct List<T>(pub Vec<T>);
   |            ^^^^ -
help: add missing generic argument
   |
16 | pub struct List<T><T>(pub Vec<T>);
   |                +++

Reproduces How Often

Is it always reproducible? -> Yes

Workarounds

manually implement the derived traits:

[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
#[serde(transparent)]
pub struct List<T>(pub Vec<T>);

impl<T> sea_orm::TryGetableFromJson for List<T> where for<'de> T: Deserialize<'de> {}

impl<T> std::convert::From<List<T>> for sea_orm::Value
where
    List<T>: Serialize,
{
    fn from(source: List<T>) -> Self {
        sea_orm::Value::Json(
            serde_json::to_value(&source)
                .ok()
                .map(|s| std::boxed::Box::new(s)),
        )
    }
}

impl<T> sea_orm::sea_query::ValueType for List<T>
where
    List<T>: DeserializeOwned,
{
    fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> {
        match v {
            sea_orm::Value::Json(Some(json)) => {
                Ok(serde_json::from_value(*json).map_err(|_| sea_orm::sea_query::ValueTypeErr)?)
            }
            _ => Err(sea_orm::sea_query::ValueTypeErr),
        }
    }

    fn type_name() -> String {
        stringify!(#ident).to_owned()
    }

    fn array_type() -> sea_orm::sea_query::ArrayType {
        sea_orm::sea_query::ArrayType::Json
    }

    fn column_type() -> sea_orm::sea_query::ColumnType {
        sea_orm::sea_query::ColumnType::Json
    }
}

impl<T> sea_orm::sea_query::Nullable for List<T> {
    fn null() -> sea_orm::Value {
        sea_orm::Value::Json(None)
    }
}

Reproducible Example

[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize, FromJsonQueryResult)]
#[serde(transparent)]
pub struct List<T>(pub Vec<T>);

Versions

❯ cargo tree | grep sea- 
├── sea-orm v0.12.10
│   ├── sea-orm-macros v0.12.10 (proc-macro)
│   │   ├── sea-bae v0.2.0 (proc-macro)
│   ├── sea-query v0.30.6
anshap1719 commented 8 months ago

@nitn3lav Will you be creating a PR for this as well?