#![deny(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
variant_size_differences
)]
#![warn(rust_2018_idioms)]
#![doc(test(attr(deny(
missing_copy_implementations,
missing_debug_implementations,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
variant_size_differences,
))))]
#![doc(test(attr(warn(rust_2018_idioms))))]
#![doc(test(no_crate_inject))]
#![doc(html_root_url = "https://docs.rs/serde_with_macros/1.2.1")]
#[allow(unused_extern_crates)]
extern crate proc_macro;
mod utils;
use crate::utils::IteratorExt as _;
use darling::{Error as DarlingError, FromField};
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use std::str::FromStr;
use syn::{
parse::Parser, punctuated::Pair, spanned::Spanned, Attribute, DeriveInput, Error, Field,
Fields, GenericArgument, ItemEnum, ItemStruct, Meta, NestedMeta, Path, PathArguments,
ReturnType, Type,
};
fn apply_function_to_struct_and_enum_fields<F>(
input: TokenStream,
function: F,
) -> Result<proc_macro2::TokenStream, Error>
where
F: Copy,
F: Fn(&mut Field) -> Result<(), String>,
{
fn apply_on_fields<F>(fields: &mut Fields, function: F) -> Result<(), Error>
where
F: Fn(&mut Field) -> Result<(), String>,
{
match fields {
Fields::Unit => Ok(()),
Fields::Named(ref mut fields) => fields
.named
.iter_mut()
.map(|field| function(field).map_err(|err| Error::new(field.span(), err)))
.collect_error(),
Fields::Unnamed(ref mut fields) => fields
.unnamed
.iter_mut()
.map(|field| function(field).map_err(|err| Error::new(field.span(), err)))
.collect_error(),
}
}
if let Ok(mut input) = syn::parse::<ItemStruct>(input.clone()) {
apply_on_fields(&mut input.fields, function)?;
Ok(quote!(#input))
} else if let Ok(mut input) = syn::parse::<ItemEnum>(input) {
input
.variants
.iter_mut()
.map(|variant| apply_on_fields(&mut variant.fields, function))
.collect_error()?;
Ok(quote!(#input))
} else {
Err(Error::new(
Span::call_site(),
"The attribute can only be applied to struct or enum definitions.",
))
}
}
fn apply_function_to_struct_and_enum_fields_darling<F>(
input: TokenStream,
function: F,
) -> Result<proc_macro2::TokenStream, DarlingError>
where
F: Copy,
F: Fn(&mut Field) -> Result<(), DarlingError>,
{
fn apply_on_fields<F>(fields: &mut Fields, function: F) -> Result<(), DarlingError>
where
F: Fn(&mut Field) -> Result<(), DarlingError>,
{
match fields {
Fields::Unit => Ok(()),
Fields::Named(ref mut fields) => {
let errors: Vec<DarlingError> = fields
.named
.iter_mut()
.map(|field| function(field).map_err(|err| err.with_span(&field)))
.filter_map(|res| match res {
Err(e) => Some(e),
Ok(()) => None,
})
.collect();
if errors.is_empty() {
Ok(())
} else {
Err(DarlingError::multiple(errors))
}
}
Fields::Unnamed(ref mut fields) => {
let errors: Vec<DarlingError> = fields
.unnamed
.iter_mut()
.map(|field| function(field).map_err(|err| err.with_span(&field)))
.filter_map(|res| match res {
Err(e) => Some(e),
Ok(()) => None,
})
.collect();
if errors.is_empty() {
Ok(())
} else {
Err(DarlingError::multiple(errors))
}
}
}
}
if let Ok(mut input) = syn::parse::<ItemStruct>(input.clone()) {
apply_on_fields(&mut input.fields, function)?;
Ok(quote!(#input))
} else if let Ok(mut input) = syn::parse::<ItemEnum>(input) {
let errors: Vec<DarlingError> = input
.variants
.iter_mut()
.map(|variant| apply_on_fields(&mut variant.fields, function))
.filter_map(|res| match res {
Err(e) => Some(e),
Ok(()) => None,
})
.collect();
if errors.is_empty() {
Ok(quote!(#input))
} else {
Err(DarlingError::multiple(errors))
}
} else {
Err(DarlingError::custom(
"The attribute can only be applied to struct or enum definitions.",
)
.with_span(&Span::call_site()))
}
}
#[proc_macro_attribute]
pub fn skip_serializing_none(_args: TokenStream, input: TokenStream) -> TokenStream {
let res = match apply_function_to_struct_and_enum_fields(
input,
skip_serializing_none_add_attr_to_field,
) {
Ok(res) => res,
Err(err) => err.to_compile_error(),
};
TokenStream::from(res)
}
fn skip_serializing_none_add_attr_to_field(field: &mut Field) -> Result<(), String> {
if let Type::Path(path) = &field.ty {
if is_std_option(&path.path) {
let has_skip_serializing_if =
field_has_attribute(&field, "serde", "skip_serializing_if");
let mut has_always_attr = false;
field.attrs.retain(|attr| {
let has_attr = attr.path.is_ident("serialize_always");
has_always_attr |= has_attr;
!has_attr
});
if has_always_attr && has_skip_serializing_if {
let mut msg = r#"The attributes `serialize_always` and `serde(skip_serializing_if = "...")` cannot be used on the same field"#.to_string();
if let Some(ident) = &field.ident {
msg += ": `";
msg += &ident.to_string();
msg += "`";
}
msg += ".";
return Err(msg);
}
if has_skip_serializing_if || has_always_attr {
return Ok(());
}
let attr_tokens = quote!(
#[serde(skip_serializing_if = "Option::is_none")]
);
let parser = Attribute::parse_outer;
let attrs = parser
.parse2(attr_tokens)
.expect("Static attr tokens should not panic");
field.attrs.extend(attrs);
} else {
let has_attr = field
.attrs
.iter()
.any(|attr| attr.path.is_ident("serialize_always"));
if has_attr {
return Err(
"`serialize_always` may only be used on fields of type `Option`.".into(),
);
}
}
}
Ok(())
}
fn is_std_option(path: &Path) -> bool {
(path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == "Option")
|| (path.segments.len() == 3
&& (path.segments[0].ident == "std" || path.segments[0].ident == "core")
&& path.segments[1].ident == "option"
&& path.segments[2].ident == "Option")
}
#[allow(clippy::cmp_owned)]
fn field_has_attribute(field: &Field, namespace: &str, name: &str) -> bool {
for attr in &field.attrs {
if attr.path.is_ident(namespace) {
if let Ok(expr) = attr.parse_meta() {
if let Meta::List(expr) = expr {
for expr in expr.nested {
if let NestedMeta::Meta(Meta::NameValue(expr)) = expr {
if let Some(ident) = expr.path.get_ident() {
if ident.to_string() == name {
return true;
}
}
}
}
}
}
}
}
false
}
#[proc_macro_attribute]
pub fn serde_as(_args: TokenStream, input: TokenStream) -> TokenStream {
let res =
match apply_function_to_struct_and_enum_fields_darling(input, serde_as_add_attr_to_field) {
Ok(res) => res,
Err(err) => err.write_errors(),
};
TokenStream::from(res)
}
fn serde_as_add_attr_to_field(field: &mut Field) -> Result<(), DarlingError> {
#[derive(FromField, Debug)]
#[darling(attributes(serde_as))]
struct SerdeAsOptions {
#[darling(rename = "as", map = "parse_type_from_string", default)]
r#as: Option<Result<Type, Error>>,
#[darling(map = "parse_type_from_string", default)]
deserialize_as: Option<Result<Type, Error>>,
#[darling(map = "parse_type_from_string", default)]
serialize_as: Option<Result<Type, Error>>,
}
impl SerdeAsOptions {
fn has_any_set(&self) -> bool {
self.r#as.is_some() || self.deserialize_as.is_some() || self.serialize_as.is_some()
}
}
#[derive(FromField, Debug)]
#[darling(attributes(serde), allow_unknown_fields)]
struct SerdeWithOptions {
#[darling(default)]
with: Option<String>,
#[darling(default)]
deserialize_with: Option<String>,
#[darling(default)]
serialize_with: Option<String>,
}
impl SerdeWithOptions {
fn has_any_set(&self) -> bool {
self.with.is_some() || self.deserialize_with.is_some() || self.serialize_with.is_some()
}
}
let serde_as_options = SerdeAsOptions::from_field(field)?;
let serde_with_options = SerdeWithOptions::from_field(field)?;
let serde_as_idx = field.attrs.iter().enumerate().find_map(|(idx, attr)| {
if attr.path.is_ident("serde_as") {
Some(idx)
} else {
None
}
});
if serde_as_idx.is_none() {
return Ok(());
}
let serde_as_idx = serde_as_idx.expect("Just checked that the value is Some");
field.attrs.remove(serde_as_idx);
if serde_as_options.has_any_set() && serde_with_options.has_any_set() {
return Err(DarlingError::custom("Cannot combine `serde_as` with serde's `with`, `deserialize_with`, or `serialize_with`."));
}
if serde_as_options.r#as.is_some() && serde_as_options.deserialize_as.is_some() {
return Err(DarlingError::custom("Cannot combine `as` with `deserialize_as`. Use `serialize_as` to specify different serialization code."));
} else if serde_as_options.r#as.is_some() && serde_as_options.serialize_as.is_some() {
return Err(DarlingError::custom("Cannot combine `as` with `serialize_as`. Use `deserialize_as` to specify different deserialization code."));
}
if let Some(Ok(type_)) = serde_as_options.r#as {
let attr_tokens = proc_macro2::TokenStream::from_str(&*format!(
r##"#[serde(with = "::serde_with::As::<{}>")]"##,
replace_infer_type_with_type(type_, &syn::parse_str("::serde_with::Same").unwrap())
.to_token_stream()
))
.unwrap();
let attrs = Attribute::parse_outer
.parse2(attr_tokens)
.expect("Static attr tokens should not panic");
field.attrs.extend(attrs);
}
if let Some(Ok(type_)) = serde_as_options.deserialize_as {
let attr_tokens = proc_macro2::TokenStream::from_str(&*format!(
r##"#[serde(deserialize_with = "::serde_with::As::<{}>::deserialize")]"##,
replace_infer_type_with_type(type_, &syn::parse_str("::serde_with::Same").unwrap())
.to_token_stream()
))
.unwrap();
let attrs = Attribute::parse_outer
.parse2(attr_tokens)
.expect("Static attr tokens should not panic");
field.attrs.extend(attrs);
}
if let Some(Ok(type_)) = serde_as_options.serialize_as {
let attr_tokens = proc_macro2::TokenStream::from_str(&*format!(
r##"#[serde(serialize_with = "::serde_with::As::<{}>::serialize")]"##,
replace_infer_type_with_type(type_, &syn::parse_str("::serde_with::Same").unwrap())
.to_token_stream()
))
.unwrap();
let attrs = Attribute::parse_outer
.parse2(attr_tokens)
.expect("Static attr tokens should not panic");
field.attrs.extend(attrs);
}
Ok(())
}
fn parse_type_from_string(s: String) -> Option<Result<Type, Error>> {
Some(syn::parse_str(&*s))
}
fn replace_infer_type_with_type(to_replace: Type, replacement: &Type) -> Type {
match to_replace {
Type::Infer(_) => replacement.clone(),
Type::Array(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Array(inner)
}
Type::Group(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Group(inner)
}
Type::Never(inner) => Type::Never(inner),
Type::Paren(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Paren(inner)
}
Type::Path(mut inner) => {
match inner.path.segments.pop() {
Some(Pair::End(mut t)) | Some(Pair::Punctuated(mut t, _)) => {
t.arguments = match t.arguments {
PathArguments::None => PathArguments::None,
PathArguments::AngleBracketed(mut inner) => {
inner.args = inner
.args
.into_iter()
.map(|generic_argument| match generic_argument {
GenericArgument::Type(type_) => GenericArgument::Type(
replace_infer_type_with_type(type_, replacement),
),
ga => ga,
})
.collect();
PathArguments::AngleBracketed(inner)
}
PathArguments::Parenthesized(mut inner) => {
inner.inputs = inner
.inputs
.into_iter()
.map(|type_| replace_infer_type_with_type(type_, replacement))
.collect();
inner.output = match inner.output {
ReturnType::Type(arrow, mut type_) => {
*type_ = replace_infer_type_with_type(*type_, replacement);
ReturnType::Type(arrow, type_)
}
default => default,
};
PathArguments::Parenthesized(inner)
}
};
inner.path.segments.push(t);
}
None => {}
}
Type::Path(inner)
}
Type::Ptr(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Ptr(inner)
}
Type::Reference(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Reference(inner)
}
Type::Slice(mut inner) => {
*inner.elem = replace_infer_type_with_type(*inner.elem, replacement);
Type::Slice(inner)
}
Type::Tuple(mut inner) => {
inner.elems = inner
.elems
.into_pairs()
.map(|pair| match pair {
Pair::Punctuated(type_, p) => {
Pair::Punctuated(replace_infer_type_with_type(type_, replacement), p)
}
Pair::End(type_) => Pair::End(replace_infer_type_with_type(type_, replacement)),
})
.collect();
Type::Tuple(inner)
}
type_ => type_,
}
}
#[proc_macro_derive(DeserializeFromStr)]
pub fn derive_deserialize_fromstr(item: TokenStream) -> TokenStream {
let res = match deserialize_fromstr(item) {
Ok(res) => res,
Err(err) => err.to_compile_error(),
};
TokenStream::from(res)
}
fn deserialize_fromstr(item: TokenStream) -> Result<proc_macro2::TokenStream, Error> {
let input = syn::parse::<DeriveInput>(item)?;
let ident = input.ident;
Ok(quote! {
#[automatically_derived]
impl<'de> serde::Deserialize<'de> for #ident
where
Self: std::str::FromStr,
<Self as std::str::FromStr>::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Helper<S>(std::marker::PhantomData<S>);
impl<'de, S> serde::de::Visitor<'de> for Helper<S>
where
S: std::str::FromStr,
<S as std::str::FromStr>::Err: std::fmt::Display,
{
type Value = S;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "string")
}
fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
value.parse::<Self::Value>().map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_str(Helper(std::marker::PhantomData))
}
}
})
}
#[proc_macro_derive(SerializeDisplay)]
pub fn derive_serialize_display(item: TokenStream) -> TokenStream {
let res = match serialize_display(item) {
Ok(res) => res,
Err(err) => err.to_compile_error(),
};
TokenStream::from(res)
}
fn serialize_display(item: TokenStream) -> Result<proc_macro2::TokenStream, Error> {
let input = syn::parse::<DeriveInput>(item)?;
let ident = input.ident;
Ok(quote! {
#[automatically_derived]
impl<'de> serde::Serialize for #ident
where
Self: std::fmt::Display,
{
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
})
}