diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2baa9f..89ac783c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). and ignores field type itself. - The `Into` derive now uses `#[into()]` instead of `#[into(types())]` and ignores field type itself. +- The `Into` derive now generates separate impls for each field whenever the `#[into(...)]` + attribute is applied to it. ([#291](https://github.com/JelteF/derive_more/pull/291)) - Importing a derive macro now also import its corresponding trait. - The `Error` derive is updated with changes to the `error_generic_member_access` unstable feature for nightly users. ([#200](https://github.com/JelteF/derive_more/pull/200), diff --git a/impl/doc/into.md b/impl/doc/into.md index 63265b62..81d47308 100644 --- a/impl/doc/into.md +++ b/impl/doc/into.md @@ -109,6 +109,90 @@ assert_eq!(5_i128, Mass::::new(5).into()); ``` +### Fields + +The `#[into]` attribute can also be applied to specific fields of a struct. + +```rust +# use derive_more::Into; +# +#[derive(Into)] +struct Data { + id: i32, + #[into] + raw: f64 +} + +assert_eq!(42.0, Data { id: 1, raw: 42.0 }.into()); +``` + +In such cases, no conversion into a tuple of all fields is generated, unless +an explicit struct attribute is present. + +```rust +# use derive_more::Into; +# +#[derive(Into)] +#[into] +struct Data { + id: i32, + #[into] + raw: f64 +} + +assert_eq!(42.0, Data { id: 1, raw: 42.0 }.into()); +assert_eq!((1, 42.0), Data { id: 1, raw: 42.0 }.into()); +``` + +The `#[into()]` syntax can be used on fields as well. + +```rust +# use std::marker::PhantomData; +# use derive_more::Into; +# struct Whatever; +# +#[derive(Into, Clone)] +#[into(owned, ref((u8, str)), ref_mut)] +struct Foo { + #[into(owned(u64), ref)] + a: u8, + b: String, + #[into(skip)] + _c: PhantomData, +} + +let mut foo = Foo { a: 1, b: "string".to_owned(), _c: PhantomData }; + +assert_eq!((1_u8, "string".to_owned()), foo.clone().into()); +assert_eq!((&1_u8, "string"), <(&u8, &str)>::from(&foo)); +assert_eq!((&mut 1_u8, &mut "string".to_owned()), <(&mut u8, &mut String)>::from(&mut foo)); +assert_eq!(1_u64, foo.clone().into()); +assert_eq!(&1_u8, <&u8>::from(&foo)); +``` + +Fields, having specific conversions into them, can also be skipped for top-level +tuple conversions. + +```rust +# use derive_more::Into; + +#[derive(Into)] +#[into(ref((str, f64)))] +struct Foo { + #[into(ref)] + #[into(skip)] + a: u8, + b: String, + c: f64, +} + +let foo = Foo { a: 1, b: "string".to_owned(), c: 3.0 }; + +assert_eq!(("string", &3.0), (&foo).into()); +assert_eq!(&1_u8, <&u8>::from(&foo)); +``` + + ## Enums diff --git a/impl/src/into.rs b/impl/src/into.rs index 347c97ad..85f75ab0 100644 --- a/impl/src/into.rs +++ b/impl/src/into.rs @@ -3,7 +3,7 @@ use std::{ any::{Any, TypeId}, borrow::Cow, - iter, + iter, slice, }; use proc_macro2::{Span, TokenStream}; @@ -18,7 +18,7 @@ use syn::{ use crate::utils::{ attr::{self, ParseMultiple as _}, - polyfill, Either, FieldsExt as _, Spanning, + polyfill, Either, FieldsExt, Spanning, }; /// Expands an [`Into`] derive macro. @@ -37,110 +37,270 @@ pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result Some(Ok(( - &f.ty, - f.ident.as_ref().map_or_else( - || Either::Right(syn::Index::from(i)), - Either::Left, - ), - ))), - Ok(Some(_)) => None, - Err(e) => Some(Err(e)), - }, - ) + .map(|(i, f)| { + let field_attr = FieldAttribute::parse_attrs_with( + &f.attrs, + &attr_name, + &ConsiderLegacySyntax { + fields: slice::from_ref(f), + }, + )? + .map(Spanning::into_inner); + + let skip = field_attr + .as_ref() + .map(|attr| attr.skip.is_some()) + .unwrap_or(false); + + let convs = field_attr.and_then(|attr| attr.convs); + + Ok(((i, f, skip), convs)) + }) .collect::>>()?; - let (fields_tys, fields_idents): (Vec<_>, Vec<_>) = fields.into_iter().unzip(); - let (fields_tys, fields_idents) = (&fields_tys, &fields_idents); + let (fields, fields_convs): (Vec<_>, Vec<_>) = fields_data.into_iter().unzip(); + + let struct_attr = struct_attr.or_else(|| { + fields_convs + .iter() + .all(Option::is_none) + .then(ConversionsAttribute::default) + .map(Either::Right) + }); - let expand = |tys: Option>, r: bool, m: bool| { - let Some(tys) = tys else { - return Either::Left(iter::empty()); - }; + let mut expansions: Vec<_> = fields + .iter() + .zip(fields_convs) + .filter_map(|(&(i, field, _), convs)| { + convs.map(|convs| Expansion { + input_ident: &input.ident, + input_generics: &input.generics, + fields: vec![(i, field)], + convs, + }) + }) + .collect(); + if let Some(attr) = struct_attr { + expansions.push(Expansion { + input_ident: &input.ident, + input_generics: &input.generics, + fields: fields + .into_iter() + .filter_map(|(i, f, skip)| (!skip).then_some((i, f))) + .collect(), + convs: attr.into(), + }); + } + expansions.into_iter().map(Expansion::expand).collect() +} - let lf = - r.then(|| syn::Lifetime::new("'__derive_more_into", Span::call_site())); - let r = r.then(token::And::default); - let m = m.then(token::Mut::default); +/// Expansion of an [`Into`] derive macro, generating [`From`] implementations for a struct. +struct Expansion<'a> { + /// [`syn::Ident`] of the struct. + input_ident: &'a syn::Ident, - let gens = if let Some(lf) = lf.clone() { - let mut gens = input.generics.clone(); - gens.params.push(syn::LifetimeParam::new(lf).into()); - Cow::Owned(gens) - } else { - Cow::Borrowed(&input.generics) - }; + /// [`syn::Generics`] of the struct. + input_generics: &'a syn::Generics, - Either::Right( - if tys.is_empty() { - Either::Left(iter::once(syn::Type::Tuple(syn::TypeTuple { - paren_token: token::Paren::default(), - elems: fields_tys.iter().cloned().cloned().collect(), - }))) - } else { - Either::Right(tys.into_iter()) - } - .map(move |ty| { - let tys = fields_tys.validate_type(&ty)?.collect::>(); + /// Fields to convert from, along with their indices. + fields: Vec<(usize, &'a syn::Field)>, + + /// Conversions to be generated. + convs: ConversionsAttribute, +} + +impl<'a> Expansion<'a> { + fn expand(self) -> syn::Result { + let Self { + input_ident, + input_generics, + fields, + convs, + } = self; + + let fields_idents: Vec<_> = fields + .iter() + .map(|(i, f)| { + f.ident + .as_ref() + .map_or_else(|| Either::Left(syn::Index::from(*i)), Either::Right) + }) + .collect(); + let fields_tys: Vec<_> = fields.iter().map(|(_, f)| &f.ty).collect(); + let fields_tuple = syn::Type::Tuple(syn::TypeTuple { + paren_token: token::Paren::default(), + elems: fields_tys.iter().cloned().cloned().collect(), + }); + + [ + (&convs.owned, false, false), + (&convs.r#ref, true, false), + (&convs.ref_mut, true, true), + ] + .into_iter() + .filter_map(|(out_tys, ref_, mut_)| { + out_tys.as_ref().map(|out_tys| { + let lf = ref_.then(|| { + syn::Lifetime::new("'__derive_more_into", Span::call_site()) + }); + let r = ref_.then(token::And::default); + let m = mut_.then(token::Mut::default); + + let gens = if let Some(lf) = lf.clone() { + let mut gens = input_generics.clone(); + gens.params.push(syn::LifetimeParam::new(lf).into()); + Cow::Owned(gens) + } else { + Cow::Borrowed(input_generics) + }; let (impl_gens, _, where_clause) = gens.split_for_impl(); - let (_, ty_gens, _) = input.generics.split_for_impl(); - - Ok(quote! { - #[automatically_derived] - impl #impl_gens ::core::convert::From<#r #lf #m #ident #ty_gens> - for ( #( #r #lf #m #tys ),* ) #where_clause - { - #[inline] - fn from(value: #r #lf #m #ident #ty_gens) -> Self { - (#( - <#r #m #tys as ::core::convert::From<_>>::from( - #r #m value. #fields_idents - ) - ),*) + let (_, ty_gens, _) = input_generics.split_for_impl(); + + if out_tys.is_empty() { + Either::Left(iter::once(&fields_tuple)) + } else { + Either::Right(out_tys.iter()) + }.map(|out_ty| { + let tys: Vec<_> = fields_tys.validate_type(out_ty)?.collect(); + + Ok(quote! { + #[automatically_derived] + impl #impl_gens ::core::convert::From<#r #lf #m #input_ident #ty_gens> + for ( #( #r #lf #m #tys ),* ) #where_clause + { + #[inline] + fn from(value: #r #lf #m #input_ident #ty_gens) -> Self { + (#( + <#r #m #tys as ::core::convert::From<_>>::from( + #r #m value. #fields_idents + ) + ),*) + } } - } + }) }) - }), - ) - }; - - [ - expand(attr.owned, false, false), - expand(attr.r#ref, true, false), - expand(attr.ref_mut, true, true), - ] - .into_iter() - .flatten() - .collect() + .collect::>() + }) + }) + .collect() + } } /// Representation of an [`Into`] derive macro struct container attribute. /// /// ```rust,ignore +/// #[into] +/// #[into()] +/// #[into(owned(), ref(), ref_mut())] +/// ``` +type StructAttribute = Either; + +impl From for ConversionsAttribute { + fn from(v: StructAttribute) -> Self { + match v { + Either::Left(_) => ConversionsAttribute::default(), + Either::Right(c) => c, + } + } +} + +/// Representation of an [`Into`] derive macro field attribute. +/// +/// ```rust,ignore +/// #[into] /// #[into()] /// #[into(owned(), ref(), ref_mut())] +/// #[into(skip)] #[into(ignore)] /// ``` -#[derive(Debug, Default)] -struct StructAttribute { +#[derive(Clone, Debug)] +struct FieldAttribute { + skip: Option, + convs: Option, +} + +impl Parse for FieldAttribute { + fn parse(_: ParseStream<'_>) -> syn::Result { + unreachable!("call `attr::ParseMultiple::parse_attr_with()` instead") + } +} + +impl attr::ParseMultiple for FieldAttribute { + fn parse_attr_with( + attr: &syn::Attribute, + parser: &P, + ) -> syn::Result { + type Untyped = Either>; + impl From for FieldAttribute { + fn from(v: Untyped) -> Self { + match v { + Untyped::Left(skip) => Self { + skip: Some(skip), + convs: None, + }, + Untyped::Right(c) => Self { + skip: None, + convs: Some(match c { + Either::Left(_empty) => ConversionsAttribute::default(), + Either::Right(convs) => convs, + }), + }, + } + } + } + + Untyped::parse_attr_with(attr, parser).map(Self::from) + } + + fn merge_attrs( + prev: Spanning, + new: Spanning, + name: &syn::Ident, + ) -> syn::Result> { + let skip = attr::Skip::merge_opt_attrs( + prev.clone().map(|v| v.skip).transpose(), + new.clone().map(|v| v.skip).transpose(), + name, + )? + .map(Spanning::into_inner); + + let convs = ConversionsAttribute::merge_opt_attrs( + prev.clone().map(|v| v.convs).transpose(), + new.clone().map(|v| v.convs).transpose(), + name, + )? + .map(Spanning::into_inner); + + Ok(Spanning::new( + Self { skip, convs }, + prev.span.join(new.span).unwrap_or(prev.span), + )) + } +} + +/// Representation of an [`Into`] derive macro attribute describing specified [`Into`] conversions. +/// +/// ```rust,ignore +/// #[into()] +/// #[into(owned(), ref(), ref_mut())] +/// ``` +/// +/// For each field: +/// - [`None`] represents no conversions. +/// - Empty [`Punctuated`] represents a conversion into the field type. +#[derive(Clone, Debug)] +struct ConversionsAttribute { /// [`Type`]s wrapped into `owned(...)` or simply `#[into(...)]`. owned: Option>, @@ -151,9 +311,23 @@ struct StructAttribute { ref_mut: Option>, } -impl Parse for StructAttribute { +impl Default for ConversionsAttribute { + fn default() -> Self { + Self { + owned: Some(Punctuated::new()), + r#ref: None, + ref_mut: None, + } + } +} + +impl Parse for ConversionsAttribute { fn parse(input: ParseStream<'_>) -> syn::Result { - let mut out = Self::default(); + let mut out = Self { + owned: None, + r#ref: None, + ref_mut: None, + }; let parse_inner = |ahead, types: &mut Option<_>| { input.advance_to(&ahead); @@ -231,7 +405,7 @@ impl Parse for StructAttribute { } } -impl attr::ParseMultiple for StructAttribute { +impl attr::ParseMultiple for ConversionsAttribute { fn merge_attrs( prev: Spanning, new: Spanning, @@ -266,15 +440,19 @@ impl attr::ParseMultiple for StructAttribute { } /// [`attr::Parser`] considering legacy syntax and performing [`check_legacy_syntax()`] for a -/// [`StructAttribute`]. -struct ConsiderLegacySyntax<'a> { - /// [`syn::Fields`] of a struct, the [`StructAttribute`] is parsed for. - fields: &'a syn::Fields, +/// [`StructAttribute`] or a [`FieldAttribute`]. +struct ConsiderLegacySyntax { + /// [`syn::Field`]s the [`StructAttribute`] or [`FieldAttribute`] is parsed for. + fields: F, } -impl attr::Parser for ConsiderLegacySyntax<'_> { +impl<'a, F> attr::Parser for ConsiderLegacySyntax<&'a F> +where + F: FieldsExt + ?Sized, + &'a F: IntoIterator, +{ fn parse(&self, input: ParseStream<'_>) -> syn::Result { - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { check_legacy_syntax(input, self.fields)?; } T::parse(input) @@ -282,10 +460,11 @@ impl attr::Parser for ConsiderLegacySyntax<'_> { } /// [`Error`]ors for legacy syntax: `#[into(types(i32, "&str"))]`. -fn check_legacy_syntax( - tokens: ParseStream<'_>, - fields: &syn::Fields, -) -> syn::Result<()> { +fn check_legacy_syntax<'a, F>(tokens: ParseStream<'_>, fields: &'a F) -> syn::Result<()> +where + F: FieldsExt + ?Sized, + &'a F: IntoIterator, +{ let span = tokens.span(); let tokens = tokens.fork(); @@ -306,7 +485,7 @@ fn check_legacy_syntax( 0 => None, 1 => Some( fields - .iter() + .into_iter() .next() .unwrap_or_else(|| unreachable!("fields.len() == 1")) .ty @@ -316,7 +495,7 @@ fn check_legacy_syntax( _ => Some(format!( "({})", fields - .iter() + .into_iter() .map(|f| f.ty.to_token_stream().to_string()) .collect::>() .join(", ") diff --git a/impl/src/utils.rs b/impl/src/utils.rs index a1829c01..72f11eb4 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -1464,6 +1464,19 @@ mod spanning { } } + #[cfg(feature = "into")] + impl Spanning> { + pub(crate) fn transpose(self) -> Option> { + match self.item { + Some(item) => Some(Spanning { + item, + span: self.span, + }), + None => None, + } + } + } + impl Deref for Spanning { type Target = T; @@ -1497,6 +1510,13 @@ pub(crate) mod attr { use super::{Either, Spanning}; + #[cfg(any( + feature = "as_ref", + feature = "from", + feature = "into", + feature = "try_from" + ))] + pub(crate) use self::empty::Empty; #[cfg(any( feature = "as_ref", feature = "debug", @@ -1505,12 +1525,12 @@ pub(crate) mod attr { feature = "into", ))] pub(crate) use self::skip::Skip; + #[cfg(any(feature = "as_ref", feature = "from", feature = "try_from"))] + pub(crate) use self::types::Types; #[cfg(any(feature = "as_ref", feature = "from"))] pub(crate) use self::{ conversion::Conversion, field_conversion::FieldConversion, forward::Forward, }; - #[cfg(any(feature = "as_ref", feature = "from", feature = "try_from"))] - pub(crate) use self::{empty::Empty, types::Types}; #[cfg(feature = "try_from")] pub(crate) use self::{repr_conversion::ReprConversion, repr_int::ReprInt}; @@ -1556,6 +1576,23 @@ pub(crate) mod attr { )) } + /// Merges multiple [`Option`]al values of this attribute into a single one. + /// + /// Default implementation uses [`ParseMultiple::merge_attrs()`] when both `prev` and `new` + /// are [`Some`]. + fn merge_opt_attrs( + prev: Option>, + new: Option>, + name: &syn::Ident, + ) -> syn::Result>> { + Ok(match (prev, new) { + (Some(p), Some(n)) => Some(Self::merge_attrs(p, n, name)?), + (Some(p), None) => Some(p), + (None, Some(n)) => Some(n), + (None, None) => None, + }) + } + /// Parses this attribute from the provided multiple [`syn::Attribute`]s with the provided /// [`Parser`], merging them, and preserving their [`Span`]. /// @@ -1626,7 +1663,12 @@ pub(crate) mod attr { } } - #[cfg(any(feature = "as_ref", feature = "from", feature = "try_from"))] + #[cfg(any( + feature = "as_ref", + feature = "from", + feature = "into", + feature = "try_from" + ))] mod empty { use syn::{ parse::{Parse, ParseStream}, @@ -1640,6 +1682,7 @@ pub(crate) mod attr { /// ```rust,ignore /// #[] /// ``` + #[derive(Clone, Copy, Debug)] pub(crate) struct Empty; impl Parse for Empty { @@ -1697,6 +1740,7 @@ pub(crate) mod attr { /// ```rust,ignore /// #[(forward)] /// ``` + #[derive(Clone, Copy, Debug)] pub(crate) struct Forward; impl Parse for Forward { @@ -1825,6 +1869,7 @@ pub(crate) mod attr { /// #[(skip)] /// #[(ignore)] /// ``` + #[derive(Clone, Copy, Debug)] pub(crate) struct Skip(&'static str); impl Parse for Skip { @@ -2164,7 +2209,7 @@ mod fields_ext { } } - impl Len for Vec { + impl Len for [T] { fn len(&self) -> usize { self.len() } diff --git a/tests/compile_fail/into/mixed_field_skip_types.rs b/tests/compile_fail/into/mixed_field_skip_types.rs new file mode 100644 index 00000000..5214e57a --- /dev/null +++ b/tests/compile_fail/into/mixed_field_skip_types.rs @@ -0,0 +1,7 @@ +#[derive(derive_more::Into)] +struct Foo { + #[into(skip, i32)] + a: i32, +} + +fn main() {} diff --git a/tests/compile_fail/into/mixed_field_skip_types.stderr b/tests/compile_fail/into/mixed_field_skip_types.stderr new file mode 100644 index 00000000..d36a37ba --- /dev/null +++ b/tests/compile_fail/into/mixed_field_skip_types.stderr @@ -0,0 +1,5 @@ +error[E0412]: cannot find type `skip` in this scope + --> tests/compile_fail/into/mixed_field_skip_types.rs:3:12 + | +3 | #[into(skip, i32)] + | ^^^^ not found in this scope diff --git a/tests/compile_fail/into/multiple_skip.rs b/tests/compile_fail/into/multiple_skip.rs new file mode 100644 index 00000000..e8f3b9fc --- /dev/null +++ b/tests/compile_fail/into/multiple_skip.rs @@ -0,0 +1,8 @@ +#[derive(derive_more::Into)] +struct Foo { + #[into(skip)] + #[into(skip)] + a: i32, +} + +fn main() {} diff --git a/tests/compile_fail/into/multiple_skip.stderr b/tests/compile_fail/into/multiple_skip.stderr new file mode 100644 index 00000000..cce7720c --- /dev/null +++ b/tests/compile_fail/into/multiple_skip.stderr @@ -0,0 +1,5 @@ +error: only single `#[into(skip)]`/`#[into(ignore)]` attribute is allowed here + --> tests/compile_fail/into/multiple_skip.rs:4:5 + | +4 | #[into(skip)] + | ^ diff --git a/tests/into.rs b/tests/into.rs index 95799d2f..c77c81c2 100644 --- a/tests/into.rs +++ b/tests/into.rs @@ -1043,3 +1043,395 @@ mod multi_field { } } } + +mod with_fields { + use super::*; + + mod only { + use super::*; + + #[derive(Clone, Copy, Debug, Into)] + struct Tuple(#[into] i32, f64, #[into] f64); + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (i32, f64, f64) { + fn from(value: Tuple) -> Self { + (value.0, value.1, value.2) + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (i32, f64) { + fn from(value: Tuple) -> Self { + (value.0, value.2) + } + } + + #[test] + fn tuple() { + let foo = Tuple(1, 2.0, 3.0); + + assert_eq!(1, foo.into()); + assert_eq!(3.0, foo.into()); + } + + #[derive(Clone, Copy, Debug, Into)] + struct Struct { + #[into] + a: i32, + b: f64, + #[into] + c: f64, + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (i32, f64, f64) { + fn from(value: Struct) -> Self { + (value.a, value.b, value.c) + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (i32, f64) { + fn from(value: Struct) -> Self { + (value.a, value.c) + } + } + + #[test] + fn named() { + let foo = Struct { + a: 1, + b: 2.0, + c: 3.0, + }; + + assert_eq!(1, foo.into()); + assert_eq!(3.0, foo.into()); + } + + mod types { + use super::*; + + #[derive(Clone, Debug, Into)] + struct Tuple( + #[into(Box, Cow<'_, str>)] String, + f64, + #[into(f32, f64)] f32, + ); + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for String { + fn from(value: Tuple) -> Self { + value.0 + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (String, f64, f32) { + fn from(value: Tuple) -> Self { + (value.0, value.1, value.2) + } + } + + #[test] + fn tuple() { + let foo = Tuple("1".to_owned(), 2.0, 3.0); + + assert_eq!(Box::::from("1".to_owned()), foo.clone().into()); + assert_eq!(Cow::Borrowed("1"), Cow::::from(foo.clone())); + assert_eq!(3.0f32, foo.clone().into()); + assert_eq!(3.0f64, foo.into()); + } + + #[derive(Clone, Debug, Into)] + struct Struct { + #[into(Box, Cow<'_, str>)] + a: String, + b: f64, + #[into(f32, f64)] + c: f32, + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for String { + fn from(value: Struct) -> Self { + value.a + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (String, f64, f32) { + fn from(value: Struct) -> Self { + (value.a, value.b, value.c) + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl From for (Box, f32) { + fn from(value: Struct) -> Self { + (value.a.into(), value.c) + } + } + + #[test] + fn named() { + let foo = Struct { + a: "1".to_owned(), + b: 2.0, + c: 3.0, + }; + + assert_eq!(Box::::from("1".to_owned()), foo.clone().into()); + assert_eq!(Cow::Borrowed("1"), Cow::::from(foo.clone())); + assert_eq!(3.0f32, foo.clone().into()); + assert_eq!(3.0f64, foo.into()); + } + + mod r#ref { + use super::*; + + #[derive(Debug, Into)] + struct Tuple(#[into(ref)] String, f64, #[into(ref)] f64); + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl<'a> From<&'a Tuple> for (&'a String, &'a f64, &'a f64) { + fn from(value: &'a Tuple) -> Self { + (&value.0, &value.1, &value.2) + } + } + + #[test] + fn tuple() { + let foo = Tuple("1".to_owned(), 2.0, 3.0); + + assert_eq!(&"1".to_owned(), <&String>::from(&foo)); + assert_eq!(&3.0, <&f64>::from(&foo)); + } + + #[derive(Debug, Into)] + struct Struct { + #[into(ref)] + a: String, + b: f64, + #[into(ref)] + c: f64, + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl<'a> From<&'a Struct> for (&'a String, &'a f64, &'a f64) { + fn from(value: &'a Struct) -> Self { + (&value.a, &value.b, &value.c) + } + } + + // Asserts that macro expansion doesn't generate this impl, by producing a trait + // implementations conflict error during compilation, if it does. + impl<'a> From<&'a Struct> for (&'a String, &'a f64) { + fn from(value: &'a Struct) -> Self { + (&value.a, &value.c) + } + } + + #[test] + fn named() { + let foo = Struct { + a: "1".to_owned(), + b: 2.0, + c: 3.0, + }; + + assert_eq!(&"1".to_owned(), <&String>::from(&foo)); + assert_eq!(&3.0, <&f64>::from(&foo)); + } + + mod types { + use super::*; + + #[derive(Debug, Into)] + struct Tuple( + #[into(ref(Transmuted))] Wrapped, + #[into(ref(Wrapped))] Wrapped, + ); + + #[test] + fn tuple() { + let foo = Tuple(Wrapped(1), Wrapped(2)); + + assert_eq!(&Transmuted(1), <&Transmuted>::from(&foo)); + assert_eq!(&Wrapped(2), <&Wrapped>::from(&foo)); + } + + #[derive(Debug, Into)] + struct Struct { + #[into(ref(Transmuted))] + a: Wrapped, + #[into(ref(Wrapped))] + b: Wrapped, + } + + #[test] + fn named() { + let foo = Struct { + a: Wrapped(1), + b: Wrapped(2), + }; + + assert_eq!(&Transmuted(1), <&Transmuted>::from(&foo)); + assert_eq!(&Wrapped(2), <&Wrapped>::from(&foo)); + } + } + + mod ref_mut { + use super::*; + + #[derive(Debug, Into)] + struct Tuple(#[into(ref_mut)] i32, f64, #[into(ref_mut)] f64); + + #[test] + fn tuple() { + let mut foo = Tuple(1, 2.0, 3.0); + + assert_eq!(&mut 1, <&mut i32>::from(&mut foo)); + assert_eq!(&mut 3.0, <&mut f64>::from(&mut foo)); + } + + #[derive(Debug, Into)] + struct Struct { + #[into(ref_mut)] + a: i32, + b: f64, + #[into(ref_mut)] + c: f64, + } + + #[test] + fn named() { + let mut foo = Struct { + a: 1, + b: 2.0, + c: 3.0, + }; + + assert_eq!(&mut 1, <&mut i32>::from(&mut foo)); + assert_eq!(&mut 3.0, <&mut f64>::from(&mut foo)); + } + + mod types { + use super::*; + + #[derive(Debug, Into)] + struct Tuple( + #[into(ref_mut(Transmuted))] Wrapped, + #[into(ref_mut(Wrapped))] Wrapped, + ); + + #[test] + fn tuple() { + let mut foo = Tuple(Wrapped(1), Wrapped(2)); + + assert_eq!( + &mut Transmuted(1), + <&mut Transmuted>::from(&mut foo), + ); + assert_eq!( + &mut Wrapped(2), + <&mut Wrapped>::from(&mut foo), + ); + } + + #[derive(Debug, Into)] + struct Struct { + #[into(ref_mut(Transmuted))] + a: Wrapped, + #[into(ref_mut(Wrapped))] + b: Wrapped, + } + + #[test] + fn named() { + let mut foo = Struct { + a: Wrapped(1), + b: Wrapped(2), + }; + + assert_eq!( + &mut Transmuted(1), + <&mut Transmuted>::from(&mut foo), + ); + assert_eq!( + &mut Wrapped(2), + <&mut Wrapped>::from(&mut foo), + ); + } + } + } + } + } + } + + mod mixed { + use super::*; + + #[derive(Debug, Into)] + #[into(ref((Wrapped, Transmuted)))] + struct Tuple( + #[into(owned, ref(Transmuted))] Wrapped, + #[into(skip)] + #[into(ref)] + Wrapped, + #[into(ref_mut(Wrapped, Transmuted))] Wrapped, + ); + + #[test] + fn tuple() { + let mut foo = Tuple(Wrapped(1), Wrapped(2.0), Wrapped(3.0)); + + assert_eq!(&Transmuted(1), <&Transmuted>::from(&foo)); + assert_eq!(&mut Transmuted(3.0), <&mut Transmuted>::from(&mut foo)); + assert_eq!(&mut Wrapped(3.0), <&mut Wrapped>::from(&mut foo)); + assert_eq!((&Wrapped(1), &Transmuted(3.0)), (&foo).into()); + assert_eq!(&Wrapped(2.0), <&Wrapped>::from(&foo)); + assert_eq!(Wrapped(1), foo.into()); + } + + #[derive(Debug, Into)] + #[into(ref((Wrapped, Transmuted)))] + struct Struct { + #[into(owned, ref(Transmuted))] + a: Wrapped, + #[into(skip)] + #[into(ref)] + b: Wrapped, + #[into(ref_mut(Wrapped, Transmuted))] + c: Wrapped, + } + + #[test] + fn named() { + let mut foo = Struct { + a: Wrapped(1), + b: Wrapped(2.0), + c: Wrapped(3.0), + }; + + assert_eq!(&Transmuted(1), <&Transmuted>::from(&foo)); + assert_eq!(&mut Transmuted(3.0), <&mut Transmuted>::from(&mut foo)); + assert_eq!(&mut Wrapped(3.0), <&mut Wrapped>::from(&mut foo)); + assert_eq!((&Wrapped(1), &Transmuted(3.0)), (&foo).into()); + assert_eq!(&Wrapped(2.0), <&Wrapped>::from(&foo)); + assert_eq!(Wrapped(1), foo.into()); + } + } +}