thedodd / wither

An ODM for MongoDB built on the official MongoDB Rust driver.
https://docs.rs/wither
Other
325 stars 40 forks source link

Allow models to use generics. #40

Closed magiclen closed 4 years ago

magiclen commented 5 years ago

37 is a solution for modelizing a structure which uses generics like this,

#[derive(Debug, Serialize, Deserialize, Model)]
struct Person<'a> {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<ObjectId>,
    name: Cow<'a, str>
}

But it still does not support a structure whose serde::ser::Deserialize<'de> trait is not implemented by the macro, but by manual.

For example:

#[derive(Debug, Serialize, Model)]
struct Person<'a> {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<ObjectId>,
    name: Cow<'a, str>,
}

struct StringVisitor;

impl<'de> serde::de::Visitor<'de> for StringVisitor {
    type Value = Person<'de>;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("a name")
    }

    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error {
        Ok(Person {
            id: None,
            name: Cow::Borrowed(v),
        })
    }

    fn visit_string<E>(self, v: String) -> Result<Self::Value, E> where E: serde::de::Error {
        Ok(Person {
            id: None,
            name: Cow::Owned(v),
        })
    }
}

impl<'de> serde::Deserialize<'de> for Person<'de> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
        deserializer.deserialize_str(StringVisitor)
    }
}

To make the above code work, we need a new attribute meta to assign a lifetime that is used for 'de. Like the following code,

#[derive(Debug, Serialize, Model)]
#[model(de = "'a")]
struct Person<'a> {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    id: Option<ObjectId>,
    name: Cow<'a, str>,
}
thedodd commented 4 years ago

See discussion in #37.