use crate::HELPER_OPTIONABLE_IDENT;
use crate::parsed_input::{StructParsed, StructType};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
use std::fmt;
use syn::token::PathSep;
use syn::{Attribute, Error, GenericArgument, PathArguments, Type, TypePath};
pub(crate) fn error<S: AsRef<str> + fmt::Display, T>(msg: S) -> syn::Result<T> {
Err(Error::new(Span::call_site(), msg))
}
pub(crate) fn destructure(
fields: &StructParsed,
prefix: &TokenStream,
) -> Result<TokenStream, Error> {
Ok(match fields.struct_type {
StructType::Named => {
let fields = fields
.fields
.iter()
.map(|f| {
let ident = f.field.ident.as_ref().ok_or::<Error>(
error::<_, Error>(format!(
"expected field name but none present for {f:?}"
))
.unwrap_err(),
)?;
let prefixed_ident = format_ident!("{1}{0}", ident, prefix.to_string());
Ok::<_, Error>(quote! {#ident: #prefixed_ident})
})
.collect::<Result<Vec<_>, _>>()?;
quote! {{#(#fields),*}}
}
StructType::Unnamed => {
let indices: Vec<_> = (0..fields.fields.len())
.map(|i| {
let prefixed = format_ident!("{1}{0}", i, prefix.to_string());
quote! {#prefixed}
})
.collect();
quote! {(#(#indices),*)}
}
StructType::Unit => quote! {},
})
}
pub(crate) fn struct_wrapper(
tokens: impl IntoIterator<Item = TokenStream>,
struct_name_type: &StructType,
) -> TokenStream {
let tokens = tokens.into_iter();
match struct_name_type {
StructType::Named => quote!({#(#tokens),*}),
StructType::Unnamed => quote!((#(#tokens),*)),
StructType::Unit => quote!(),
}
}
pub(crate) fn error_on_helper_attributes(
attrs: &[Attribute],
err_msg: &'static str,
) -> syn::Result<()> {
if attrs
.iter()
.filter(|attr| attr.path().is_ident(HELPER_OPTIONABLE_IDENT))
.collect::<Vec<_>>()
.is_empty()
{
Ok(())
} else {
error(err_msg)
}
}
pub(crate) fn type_path_replace_crate(ty: &mut Type, replacement: &Ident) {
if let Type::Path(ty_path) = ty {
if let Some(first_path_segment) = ty_path.path.segments.first_mut()
&& first_path_segment.ident == "crate"
{
first_path_segment.ident = replacement.clone();
ty_path.path.leading_colon = Some(PathSep::default());
}
ty_path.path.segments.iter_mut().for_each(|p| {
if let PathArguments::AngleBracketed(args) = &mut p.arguments {
args.args.iter_mut().for_each(|arg| {
if let GenericArgument::Type(ty) = arg {
type_path_replace_crate(ty, replacement);
}
});
}
});
}
}
pub(crate) fn is_option(ty: &Type) -> bool {
if let Type::Path(TypePath {
qself: _qself,
path,
}) = &ty
&& {
let segments = &path.segments;
(segments.len() == 1 && segments[0].ident == "Option")
|| (segments.len() == 2
&& segments[0].ident == "option"
&& segments[1].ident == "Option")
|| (segments.len() == 3
&& (segments[0].ident == "core" || segments[0].ident == "std")
&& segments[1].ident == "option"
&& segments[2].ident == "Option")
}
{
true
} else {
false
}
}
pub(crate) fn is_serde(path: &str) -> bool {
path == "Deserialize" || path == "Serialize" || {
let mut path = path.to_owned();
path.retain(|c| !c.is_whitespace());
path == "serde::Deserialize" || path == "serde::Serialize"
}
}
pub(crate) fn is_serialize(path: &str) -> bool {
path == "Serialize" || {
let mut path = path.to_owned();
path.retain(|c| !c.is_whitespace());
path == "serde::Serialize"
}
}