fromit 0.1.2

A super powerful macro for generating new structs with getters, setters, and `From` or `TryFrom` implementation based on the given struct.
Documentation
use super::*;

pub(crate) fn generate_into(
  src_name: &syn::Ident,
  opts: &StructOpts,
  final_generics: &FinalGenerics,
) -> syn::Result<proc_macro2::TokenStream> {
  if opts.converter.try_into.is_none() && opts.converter.into.is_none() {
    return Ok(quote!());
  }
  let (try_, style, error) = match (&opts.converter.try_into, &opts.converter.into) {
    (None, None) => return Ok(quote!()),
    (None, Some(into)) => (false, *into, None),
    (Some(try_into), None) => (true, try_into.style, try_into.error.clone()),
    (Some(_), Some(_)) => {
      return Err(syn::Error::new_spanned(
        &opts.name,
        "Cannot have both `try_from` and `from`",
      ))
    }
  };

  let mut try_into_fields = Vec::new();
  for (src_name, field) in &opts.fields {
    let src_name = format_ident!("{}", src_name);
    if let Some(skip) = &field.skip {
      if let Some(default) = &skip.default {
        try_into_fields.push(quote! {
            #src_name: #default(),
        });
      } else {
        try_into_fields.push(quote! {
            #src_name: ::core::default::Default::default(),
        });
      }

      continue;
    }

    let name = field
      .rename
      .clone()
      .unwrap_or_else(|| format_ident!("{}", src_name));

    let src_name = format_ident!("{}", src_name);
    match &field.into {
      Some(into) => {
        let final_style = into.style.unwrap_or(style);

        let converter = if try_ {
          into
            .func
            .as_ref()
            .map(|f| quote!(#f(#final_style s.#name)?))
            .unwrap_or_else(|| {
              quote! {
                  ::core::convert::TryInto::try_into(#final_style s.#name)?
              }
            })
        } else {
          into
            .func
            .as_ref()
            .map(|f| quote!(#f(#final_style s.#name)))
            .unwrap_or_else(|| {
              quote! {
                  ::core::convert::Into::into(#final_style s.#name)
              }
            })
        };
        try_into_fields.push(quote! {
            #src_name: #converter,
        });
      }
      None => match style {
        Style::Ref => {
          if try_ {
            try_into_fields.push(quote! {
                #src_name: ::core::convert::TryInto::try_into(&s.#name)?,
            });
          } else {
            try_into_fields.push(quote! {
                #src_name: ::core::convert::Into::into(&s.#name),
            });
          }
        }
        Style::Move => {
          if try_ {
            try_into_fields.push(quote! {
                #src_name: ::core::convert::TryInto::try_into(s.#name)?,
            });
          } else {
            try_into_fields.push(quote! {
                #src_name: ::core::convert::Into::into(s.#name),
            });
          }
        }
      },
    }
  }

  let final_impl_generics = &final_generics.final_impl_generics;
  let self_ty_generics = &final_generics.ty_generics;
  let final_where_clause = &final_generics.final_where_clause;
  let src_ty_generics = &final_generics.src_ty_generics;

  let name = &opts.name;
  if try_ {
    let error = error.map(|t| quote!(#t)).unwrap_or_else(|| {
      quote!(
        ::std::boxed::Box<
          dyn ::std::error::Error + ::core::marker::Send + ::core::marker::Sync + 'static,
        >
      )
    });
    Ok(quote! {
        impl #final_impl_generics ::core::convert::TryFrom<#style #name #self_ty_generics> for #src_name #src_ty_generics #final_where_clause {
            type Error = #error;

            fn try_from(s: #style #name #self_ty_generics) -> ::core::result::Result<Self, Self::Error> {
                ::core::result::Result::Ok(Self {
                    #(#try_into_fields)*
                })
            }
        }
    })
  } else {
    Ok(quote! {
        impl #final_impl_generics ::core::convert::From<#style #name #self_ty_generics> for #src_name #src_ty_generics #final_where_clause {
            fn from(s: #style #name #self_ty_generics) -> Self {
                Self {
                    #(#try_into_fields)*
                }
            }
        }
    })
  }
}