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

Implement From for Ref type #23

Merged
merged 1 commit into from
Oct 4, 2022
Merged
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
41 changes: 39 additions & 2 deletions src/from.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Generate `From` implementations to convert variants to the top-level enum.
use quote::quote;
use syn::{Ident, ImplGenerics, TypeGenerics, WhereClause};
use syn::{Ident, ImplGenerics, Lifetime, TypeGenerics, WhereClause};

pub fn generate_from_trait_impl(
pub fn generate_from_variant_trait_impl(
type_name: &Ident,
impl_generics: &ImplGenerics,
ty_generics: &TypeGenerics,
Expand All @@ -18,3 +18,40 @@ pub fn generate_from_trait_impl(
}
}
}

pub fn generate_from_variant_trait_impl_for_ref(
ref_ty_name: &Ident,
ref_ty_lifetime: &Lifetime,
ref_impl_generics: &ImplGenerics,
ref_ty_generics: &TypeGenerics,
ty_generics: &TypeGenerics,
where_clause: &Option<&WhereClause>,
variant_name: &Ident,
struct_name: &Ident,
) -> proc_macro2::TokenStream {
quote! {
impl #ref_impl_generics From<&#ref_ty_lifetime #struct_name #ty_generics> for #ref_ty_name #ref_ty_generics #where_clause {
fn from(variant: &#ref_ty_lifetime #struct_name #ty_generics) -> Self {
Self::#variant_name(variant)
}
}
}
}

pub fn generate_from_enum_trait_impl_for_ref(
ty_name: &Ident,
ty_generics: &TypeGenerics,
ref_ty_name: &Ident,
ref_ty_lifetime: &Lifetime,
ref_impl_generics: &ImplGenerics,
ref_ty_generics: &TypeGenerics,
where_clause: &Option<&WhereClause>,
) -> proc_macro2::TokenStream {
quote! {
impl #ref_impl_generics From<&#ref_ty_lifetime #ty_name #ty_generics> for #ref_ty_name #ref_ty_generics #where_clause {
fn from(ref_to_enum: &#ref_ty_lifetime #ty_name #ty_generics) -> Self {
ref_to_enum.to_ref()
}
}
}
}
31 changes: 29 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use attributes::{IdentList, NestedMetaList};
use darling::FromMeta;
use from::generate_from_trait_impl;
use from::{
generate_from_enum_trait_impl_for_ref, generate_from_variant_trait_impl,
generate_from_variant_trait_impl_for_ref,
};
use itertools::Itertools;
use macros::generate_all_map_macros;
use proc_macro::TokenStream;
Expand Down Expand Up @@ -520,7 +523,7 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {

// Generate trait implementations.
for (variant_name, struct_name) in variant_names.iter().zip_eq(&struct_names) {
let from_impl = generate_from_trait_impl(
let from_impl = generate_from_variant_trait_impl(
type_name,
impl_generics,
ty_generics,
Expand All @@ -529,8 +532,32 @@ pub fn superstruct(args: TokenStream, input: TokenStream) -> TokenStream {
struct_name,
);
output_items.push(from_impl.into());

let from_impl_for_ref = generate_from_variant_trait_impl_for_ref(
&ref_ty_name,
&ref_ty_lifetime,
ref_impl_generics,
ref_ty_generics,
ty_generics,
where_clause,
variant_name,
struct_name,
);
output_items.push(from_impl_for_ref.into());
}

// Convert reference to top-level type to `Ref`.
let ref_from_top_level_impl = generate_from_enum_trait_impl_for_ref(
type_name,
ty_generics,
&ref_ty_name,
&ref_ty_lifetime,
ref_impl_generics,
ref_ty_generics,
where_clause,
);
output_items.push(ref_from_top_level_impl.into());

TokenStream::from_iter(output_items)
}

Expand Down
38 changes: 35 additions & 3 deletions tests/from.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::fmt::Display;
use superstruct::superstruct;

#[superstruct(variants(Good, Bad), variant_attributes(derive(Debug, PartialEq)))]
#[derive(Debug, PartialEq)]
#[superstruct(
variants(Good, Bad),
variant_attributes(derive(Debug, Clone, PartialEq))
)]
#[derive(Debug, Clone, PartialEq)]
pub struct Message<T: Display> {
#[superstruct(getter(copy))]
id: u64,
Expand All @@ -13,7 +16,7 @@ pub struct Message<T: Display> {
}

#[test]
fn generic_from() {
fn generic_from_variant() {
let message_good_variant = MessageGood {
id: 0,
good: "hello",
Expand All @@ -32,3 +35,32 @@ fn generic_from() {
assert_eq!(message_bad.id(), 1);
assert_eq!(*message_bad.bad().unwrap(), "noooooo");
}

#[test]
fn generic_ref_from() {
let message_good_variant = MessageGood {
id: 0,
good: "hello",
};
let message_bad_variant = MessageBad {
id: 1,
bad: "noooooo",
};

// Check Ref from reference to variant.
let message_good_ref = MessageRef::from(&message_good_variant);
let message_bad_ref = MessageRef::from(&message_bad_variant);

assert_eq!(message_good_ref.id(), 0);
assert_eq!(*message_good_ref.good().unwrap(), "hello");

assert_eq!(message_bad_ref.id(), 1);
assert_eq!(*message_bad_ref.bad().unwrap(), "noooooo");

// Check Ref from reference to top-level enum.
let message_good = Message::from(message_good_variant.clone());
let message_good_ref = MessageRef::from(&message_good);

assert_eq!(message_good_ref.id(), 0);
assert_eq!(*message_good_ref.good().unwrap(), "hello");
}