Compare commits

...

6 Commits
v0.2.2 ... main

3 changed files with 63 additions and 30 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "serde-inline-default" name = "serde-inline-default"
version = "0.2.2" 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"

View File

@ -1,4 +1,4 @@
# serde-inline-default [![ci](https://github.com/ByteDream/serde-inline-default/actions/workflows/ci.yml/badge.svg)](https://github.com/ByteDream/serde-inline-default/actions/workflows/ci.yml) [![crates.io](https://img.shields.io/crates/v/serde-inline-default)](https://crates.io/crates/serde-inline-default) [![docs](https://img.shields.io/docsrs/serde-inline-default)](https://docs.rs/serde-inline-default/latest/serde_inline_default/) # serde-inline-default [![ci](https://github.com/ByteDream/serde-inline-default/actions/workflows/ci.yml/badge.svg)](https://github.com/ByteDream/serde-inline-default/actions/workflows/ci.yml) [![crates.io](https://img.shields.io/crates/v/serde-inline-default)](https://crates.io/crates/serde-inline-default) [![crates.io downloads](https://img.shields.io/crates/d/serde-inline-default)](https://crates.io/crates/serde-inline-default) [![docs](https://img.shields.io/docsrs/serde-inline-default)](https://docs.rs/serde-inline-default/latest/serde_inline_default/)
Tiny crate to set default values for serde fields via inline attribute declaration. Tiny crate to set default values for serde fields via inline attribute declaration.
@ -19,7 +19,7 @@ fn value_default() -> u32 { 42 }
That can get quiet messy if you have many fields with many (different) default values. That can get quiet messy if you have many fields with many (different) default values.
This crate tries to solve this issue by providing the `#[serde_inline_default]` proc macro. This crate tries to solve this issue by providing the `#[serde_inline_default]` proc macro.
With this macro set at the struct level (_**before `#[derive(Deserialize)]`/`#[derive(Serialize)]`!, otherwise it's not working correctly**_), you can set default values via `#[serde_inline_default(...)]` for your serde fields inline, without creating an extra function. With this macro set at the struct level, you can set default values via `#[serde_inline_default(...)]` for your serde fields inline, without creating an extra function.
```rust ```rust
#[serde_inline_default] #[serde_inline_default]
@ -30,9 +30,54 @@ struct Test {
} }
``` ```
> [!IMPORTANT]
> **`#[serde_inline_default]` must be set before `#[derive(Deserialize)]`/`#[derive(Serialize)]`, otherwise it's not working correctly!**
Internally, `#[serde_inline_default(...)]` gets expanded to a function which returns the set value and the attribute is replaced with `#[serde(default = "<function name>")]`. Internally, `#[serde_inline_default(...)]` gets expanded to a function which returns the set value and the attribute is replaced with `#[serde(default = "<function name>")]`.
So this macro is just some syntax sugar for you, but can get quiet handy if you want to keep your code clean or write declarative macros / `macro_rules!`. So this macro is just some syntax sugar for you, but can get quiet handy if you want to keep your code clean or write declarative macros / `macro_rules!`.
## Alternatives
This crate isn't perfect. Thus, you might be more satisfied with alternatives `serde` provides.
With `#[serde(default)]` + `impl Default` on a struct, `serde` uses the default implementation of the struct to get default values for each field ([docs](https://serde.rs/container-attrs.html#default)):
```rust
#[derive(Deserialize)]
#[serde(default)]
struct Test {
value: u32
}
impl Default for Test {
fn default() -> Self {
Self {
value: 42
}
}
}
```
If you still need/want `serde-inline-default` features, you also can combine them with `#[serde(default))` and `impl Default`:
```rust
#[serde_inline_default]
#[derive(Deserialize)]
#[serde(default)]
struct Test {
value: u32,
#[serde_inline_default(0)]
other_value: u32,
}
impl Default for Test {
fn default() -> Self {
Self {
value: 42,
other_value: 42
}
}
}
```
## License ## License
This project is licensed under either of the following licenses, at your option: This project is licensed under either of the following licenses, at your option:

View File

@ -4,7 +4,7 @@ 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() {
@ -14,31 +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())
{
fn_name_lit.clone()
} else {
let fn_name_lit = format!("__serde_inline_default_{}_{}", item.ident, i);
let fn_name_ident = Ident::new(&fn_name_lit, Span::call_site());
let mut return_type = field.ty.clone();
// replaces most lifetimes with 'static // replaces most lifetimes with 'static
type_lifetimes_to_static(&mut return_type); type_lifetimes_to_static(&mut return_type);
inline_fns.push(quote! {
#[doc(hidden)]
#[allow(non_snake_case)]
fn #fn_name_ident () -> #return_type {
#default
}
});
let inline_fn = quote! {
#[doc(hidden)]
#[allow(non_snake_case)]
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
@ -47,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
}; };