Skip to content

Commit

Permalink
Merge pull request #144 from greyblake/generics-derive-serde
Browse files Browse the repository at this point in the history
Make it possible to derive Serialize and Deserialize on Cow<a, str>
  • Loading branch information
greyblake authored Jun 3, 2024
2 parents 67ecaa5 + 28f8820 commit 05adaa1
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 32 deletions.
34 changes: 18 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion examples/any_generics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
nutype = { path = "../../nutype" }
nutype = { path = "../../nutype", features = ["serde"] }
serde = "1.0.203"
serde_json = "1.0.117"
12 changes: 9 additions & 3 deletions examples/any_generics/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ struct NotEmpty<T>(Vec<T>);
Deref,
Borrow,
AsRef,
Serialize,
Deserialize,
// TODO
// FromStr,
// TryFrom,
// Default,
// Serialize,
// Deserialize,
// Arbitrary,
))]
struct Clarabelle<'b>(Cow<'b, str>);
struct Clarabelle<'a>(Cow<'a, str>);

fn main() {
{
Expand All @@ -44,5 +44,11 @@ fn main() {
{
let muu = Clarabelle::new(Cow::Borrowed("Muu"));
assert_eq!(muu.to_string(), "Muu");
// serialize muu with serde_json
let json = serde_json::to_string(&muu).unwrap();
assert_eq!(json, "\"Muu\"");
// deserialize muu with serde_json
let same_muu: Clarabelle = serde_json::from_str(&json).unwrap();
assert_eq!(muu, same_muu);
}
}
4 changes: 2 additions & 2 deletions nutype_macros/src/any/gen/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ fn gen_implemented_traits(
}
},
AnyIrregularTrait::SerdeSerialize => Ok(
gen_impl_trait_serde_serialize(type_name)
gen_impl_trait_serde_serialize(type_name, generics)
),
AnyIrregularTrait::SerdeDeserialize => Ok(
gen_impl_trait_serde_deserialize(type_name, inner_type, maybe_error_type_name.as_ref())
gen_impl_trait_serde_deserialize(type_name, generics, inner_type, maybe_error_type_name.as_ref())
),
AnyIrregularTrait::ArbitraryArbitrary => arbitrary::gen_impl_trait_arbitrary(type_name, inner_type, guard),
})
Expand Down
22 changes: 15 additions & 7 deletions nutype_macros/src/common/gen/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,10 @@ pub fn gen_impl_trait_from_str(
}
}

pub fn gen_impl_trait_serde_serialize(type_name: &TypeName) -> TokenStream {
pub fn gen_impl_trait_serde_serialize(type_name: &TypeName, generics: &Generics) -> TokenStream {
let type_name_str = type_name.to_string();
quote! {
impl ::serde::Serialize for #type_name {
impl #generics ::serde::Serialize for #type_name #generics {
fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error>
where
S: ::serde::Serializer
Expand All @@ -254,6 +254,7 @@ pub fn gen_impl_trait_serde_serialize(type_name: &TypeName) -> TokenStream {

pub fn gen_impl_trait_serde_deserialize(
type_name: &TypeName,
type_generics: &Generics,
inner_type: impl Into<InnerType>,
maybe_error_type_name: Option<&ErrorTypeName>,
) -> TokenStream {
Expand All @@ -276,16 +277,23 @@ pub fn gen_impl_trait_serde_deserialize(
let expecting_str = format!("tuple struct {type_name}");
let type_name_str = type_name.to_string();

// type generics + 'de lifetime for Deserialize
let all_generics = {
let mut all_generics = type_generics.clone();
all_generics.params.push(syn::parse_quote!('de));
all_generics
};

quote! {
impl<'de> ::serde::Deserialize<'de> for #type_name {
impl #all_generics ::serde::Deserialize<'de> for #type_name #type_generics {
fn deserialize<D: ::serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct __Visitor<'de> {
marker: ::std::marker::PhantomData<#type_name>,
struct __Visitor #all_generics {
marker: ::std::marker::PhantomData<#type_name #type_generics>,
lifetime: ::std::marker::PhantomData<&'de ()>,
}

impl<'de> ::serde::de::Visitor<'de> for __Visitor<'de> {
type Value = #type_name;
impl #all_generics ::serde::de::Visitor<'de> for __Visitor #all_generics {
type Value = #type_name #type_generics;

fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(formatter, #expecting_str)
Expand Down
3 changes: 2 additions & 1 deletion nutype_macros/src/float/gen/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,10 @@ fn gen_implemented_traits<T: ToTokens>(
Err(syn::Error::new(span, msg))
}
},
FloatIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name)),
FloatIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name, generics)),
FloatIrregularTrait::SerdeDeserialize => Ok(gen_impl_trait_serde_deserialize(
type_name,
generics,
inner_type,
maybe_error_type_name.as_ref(),
)),
Expand Down
3 changes: 2 additions & 1 deletion nutype_macros/src/integer/gen/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ fn gen_implemented_traits<T: ToTokens>(
}
}
}
IntegerIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name)),
IntegerIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name, generics)),
IntegerIrregularTrait::SerdeDeserialize => Ok(gen_impl_trait_serde_deserialize(
type_name,
generics,
inner_type,
maybe_error_type_name.as_ref(),
)),
Expand Down
3 changes: 2 additions & 1 deletion nutype_macros/src/string/gen/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,10 @@ fn gen_implemented_traits(
Err(syn::Error::new(span, msg))
}
},
StringIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name)),
StringIrregularTrait::SerdeSerialize => Ok(gen_impl_trait_serde_serialize(type_name, generics)),
StringIrregularTrait::SerdeDeserialize => Ok(gen_impl_trait_serde_deserialize(
type_name,
generics,
inner_type,
maybe_error_type_name.as_ref(),
)),
Expand Down
47 changes: 47 additions & 0 deletions test_suite/tests/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,53 @@ mod traits {
assert_eq!(deserialized, place);
}
}

mod with_generics {
use super::*;

// TODO: Uncomment when https://github.com/greyblake/nutype/issues/142 is fixed
// #[test]
// fn test_generic_with_serde() {
// #[nutype(
// derive(Debug, Serialize, Deserialize),
// validate(predicate = |v| v.is_empty())
// )]
// struct EmptyVec<T>(Vec<T>);

// {
// let vec = EmptyVec::new(vec![]);
// let json = serde_json::to_string(&vec).unwrap();
// assert_eq!(json, "[]");

// let same_vec: EmptyVec<u8> = serde_json::from_str(&json).unwrap();
// assert_eq!(vec, same_vec);
// }

// {
// let vec = EmptyVec::new(vec![1, 2, 3]);
// let err = serde_json::to_string(&vec).unwrap_err();
// assert_eq!(
// err.to_string(),
// "EmptyVec failed the predicate test. Expected valid EmptyVec"
// );
// }
// }

#[test]
fn serialize_and_deserialize_cow() {
#[nutype(
validate(predicate = |s| s.len() >= 3),
derive(Debug, Serialize, Deserialize, PartialEq)
)]
struct Clarabelle<'a>(Cow<'a, str>);

let muu = Clarabelle::new(Cow::Borrowed("Muu")).unwrap();
let json = serde_json::to_string(&muu).unwrap();
assert_eq!(json, "\"Muu\"");
let same_muu: Clarabelle = serde_json::from_str(&json).unwrap();
assert_eq!(muu, same_muu);
}
}
}
}

Expand Down

0 comments on commit 05adaa1

Please sign in to comment.