mirror of
https://github.com/bytedream/serde-inline-default.git
synced 2025-06-27 18:40:31 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f0f9f9799 | |||
985eca392c | |||
6eddcf38fe | |||
c5d874a3ad | |||
5d0f313523 | |||
c020a6e0e4 |
@ -1,18 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde-inline-default"
|
name = "serde-inline-default"
|
||||||
version = "0.2.0"
|
version = "0.2.3"
|
||||||
authors = ["ByteDream"]
|
authors = ["bytedream"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Serde default values via inline declaration"
|
description = "Serde default values via inline declaration"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/ByteDream/serde-inline-default"
|
repository = "https://github.com/bytedream/serde-inline-default"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["serde", "serialization"]
|
keywords = ["serde", "serialization"]
|
||||||
categories = ["encoding"]
|
categories = ["encoding"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
use crate::utils::type_lifetimes_to_static;
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_quote, ItemStruct};
|
use syn::{parse_quote, ItemStruct};
|
||||||
|
|
||||||
pub(crate) fn expand_struct(mut item: ItemStruct) -> proc_macro::TokenStream {
|
pub(crate) fn expand_struct(mut item: ItemStruct) -> proc_macro::TokenStream {
|
||||||
let mut inline_fns: Vec<(String, TokenStream, TokenStream)> = vec![];
|
let mut inline_fns: Vec<TokenStream> = vec![];
|
||||||
|
|
||||||
for (i, field) in item.fields.iter_mut().enumerate() {
|
for (i, field) in item.fields.iter_mut().enumerate() {
|
||||||
for (j, attr) in field.attrs.iter_mut().enumerate() {
|
for (j, attr) in field.attrs.iter_mut().enumerate() {
|
||||||
@ -13,27 +14,21 @@ pub(crate) fn expand_struct(mut item: ItemStruct) -> proc_macro::TokenStream {
|
|||||||
|
|
||||||
let default: TokenStream = attr.parse_args().unwrap();
|
let default: TokenStream = attr.parse_args().unwrap();
|
||||||
|
|
||||||
// we check here if a function with the exact same return value already exists. if so,
|
let fn_name_lit = format!("__serde_inline_default_{}_{}", item.ident, i);
|
||||||
// this function gets used.
|
let fn_name_ident = Ident::new(&fn_name_lit, Span::call_site());
|
||||||
let fn_name_lit = if let Some((fn_name_lit, _, _)) = inline_fns
|
let mut return_type = field.ty.clone();
|
||||||
.iter()
|
|
||||||
.find(|(_, def, _)| def.to_string() == default.to_string())
|
// replaces most lifetimes with 'static
|
||||||
{
|
type_lifetimes_to_static(&mut return_type);
|
||||||
fn_name_lit.clone()
|
|
||||||
} else {
|
inline_fns.push(quote! {
|
||||||
let fn_name_lit = format!("__serde_inline_default_{}_{}", item.ident, i);
|
#[doc(hidden)]
|
||||||
let fn_name_ident = Ident::new(&fn_name_lit, Span::call_site());
|
#[allow(non_snake_case)]
|
||||||
let return_type = &field.ty;
|
fn #fn_name_ident () -> #return_type {
|
||||||
|
#default
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let inline_fn = quote! {
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn #fn_name_ident () -> #return_type {
|
|
||||||
#default
|
|
||||||
}
|
|
||||||
};
|
|
||||||
inline_fns.push((fn_name_lit.clone(), default, inline_fn));
|
|
||||||
fn_name_lit
|
|
||||||
};
|
|
||||||
field.attrs.remove(j);
|
field.attrs.remove(j);
|
||||||
field
|
field
|
||||||
.attrs
|
.attrs
|
||||||
@ -42,10 +37,8 @@ pub(crate) fn expand_struct(mut item: ItemStruct) -> proc_macro::TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let real_inline_fns: Vec<TokenStream> =
|
|
||||||
inline_fns.into_iter().map(|(_, _, func)| func).collect();
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#( #real_inline_fns )*
|
#( #inline_fns )*
|
||||||
|
|
||||||
#item
|
#item
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ use proc_macro::TokenStream;
|
|||||||
use syn::{parse_macro_input, Item};
|
use syn::{parse_macro_input, Item};
|
||||||
|
|
||||||
mod expand;
|
mod expand;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
/// The main macro of this crate.
|
/// The main macro of this crate.
|
||||||
/// Use it to define default values of fields in structs you [`Serialize`] or [`Deserialize`].
|
/// Use it to define default values of fields in structs you [`Serialize`] or [`Deserialize`].
|
||||||
|
40
src/utils.rs
Normal file
40
src/utils.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use syn::{parse_quote, GenericArgument, PathArguments, Type};
|
||||||
|
|
||||||
|
pub(crate) fn type_lifetimes_to_static(ty: &mut Type) {
|
||||||
|
match ty {
|
||||||
|
Type::Array(array) => type_lifetimes_to_static(array.elem.as_mut()),
|
||||||
|
Type::Group(group) => type_lifetimes_to_static(&mut group.elem),
|
||||||
|
Type::Path(path) => {
|
||||||
|
for segment in &mut path.path.segments {
|
||||||
|
match &mut segment.arguments {
|
||||||
|
PathArguments::None => (),
|
||||||
|
PathArguments::AngleBracketed(angle_bracketed) => {
|
||||||
|
for arg in &mut angle_bracketed.args {
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Lifetime(lifetime) => {
|
||||||
|
*lifetime = parse_quote!('static);
|
||||||
|
}
|
||||||
|
GenericArgument::Type(ty) => type_lifetimes_to_static(ty),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathArguments::Parenthesized(parenthesized) => {
|
||||||
|
for input in &mut parenthesized.inputs {
|
||||||
|
type_lifetimes_to_static(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Ptr(ptr) => type_lifetimes_to_static(&mut ptr.elem),
|
||||||
|
Type::Reference(reference) => reference.lifetime = Some(parse_quote!('static)),
|
||||||
|
Type::Slice(slice) => type_lifetimes_to_static(&mut slice.elem),
|
||||||
|
Type::Tuple(tuple) => {
|
||||||
|
for elem in &mut tuple.elems {
|
||||||
|
type_lifetimes_to_static(elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_inline_default::serde_inline_default;
|
use serde_inline_default::serde_inline_default;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_inline_default() {
|
fn test_serde_inline_default() {
|
||||||
@ -28,3 +29,17 @@ fn test_serde_inline_default() {
|
|||||||
assert_eq!(test.inline_negative, -1337);
|
assert_eq!(test.inline_negative, -1337);
|
||||||
assert_eq!(test.string, "string".to_string());
|
assert_eq!(test.string, "string".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lifetime() {
|
||||||
|
#[serde_inline_default]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct LifetimeTest<'a> {
|
||||||
|
#[serde_inline_default("test".into())]
|
||||||
|
test_str: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let lifetime_test: LifetimeTest = serde_json::from_value(json!({})).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(lifetime_test.test_str, "test");
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user