| Crates.io | configurable_serde |
| lib.rs | configurable_serde |
| version | 0.1.0 |
| created_at | 2025-09-23 12:52:06.795581+00 |
| updated_at | 2025-09-23 12:52:06.795581+00 |
| description | A proc-macro to apply reusable serde configurations. |
| homepage | https://github.com/sfisol/configurable_serde |
| repository | https://github.com/sfisol/configurable_serde |
| max_upload_size | |
| id | 1851400 |
| size | 19,672 |
A crate providing a procedural macro to apply reusable serde configurations.
The core of this crate is the #[configure_serde] attribute macro, which
generates #[serde(...)] attributes for you.
There is also a generator macro provided create_config!, which builds a wrapper
to apply #[configure_serde] to all wrapped structs and enums.
NOTE: While it is tested and working, this is an early stage of the project so it lacks many possible configurable parameters and also may be incompatible with more complicated structs and enums.
Generate serde configuration applier:
use configurable_serde::create_config;
use serde::{Serialize, Deserialize};
// Generate serde configuration applier:
create_config! {
my_api_models,
struct_rename_all = "camelCase",
enum_rename_all = "SCREAMING_SNAKE_CASE",
skip_if_none,
deny_unknown_fields
}
// Apply defined serde configuration to multiple structs or enums:
my_api_models! {
#[derive(Serialize, Deserialize)]
struct User { // camelCase and deny_unknown_fields is applied here
name: String,
surname: Option<String>, // skip_if_none is applied here
vehicle: Vehicle,
}
#[derive(Serialize, Deserialize)]
enum Vehicle { // SCREAMING_SNAKE_CASE is applied here
Car,
Bicycle,
ElectricScooter,
}
}
So this will be expanded to:
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct User {
name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
surname: Option<String>,
vehicle: Vehicle,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE", deny_unknown_fields)]
enum Vehicle {
Car,
Bicycle,
ElectricScooter,
}
Note, that if you want to use your applier across a whole project, you need to place create_config!
before any mod directives for modules using the applier.
This is because the #[macro_export] term generated by the other macro is not recognized early enough.
Also note this may confuse the rust analyzer too. To overcome this, place your configs in a separate file, f.ex.
serde_configs.rs and import it before any usage with #[macro_use] mod serde_configs;.
rename_all: Option<String>
A general rename rule for both structs and enums.
Can be overridden by struct_rename_all or enum_rename_all.
struct_rename_all: Option<String>
A specific rename rule for struct fields.
enum_rename_all: Option<String>
A specific rename rule for enum variants.
skip_if_none: bool
If present, adds #[serde(default, skip_serializing_if = "Option::is_none")]
to any field of type Option<T>.
deny_unknown_fields: bool
If present, adds #[serde(deny_unknown_fields)] to the container.
configure_serde macrouse configure_serde::configure_serde;
use serde::{Serialize, Deserialize};
#[configure_serde(rename_all = "camelCase", skip_if_none)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User {
user_id: String,
display_name: Option<String>,
}
// The struct will be serialized with camelCase fields, and `display_name`
// will be omitted if it is `None`.
let user = User { user_id: "u-123".to_string(), display_name: None };
let json = serde_json::to_string(&user).unwrap();
assert_eq!(json, r#"{"userId":"u-123"}"#);
create_configYou can define your own applier manually. It is just a declarative macro (macro_rules!) that applies the
#[configure_serde] attribute with your desired settings.
use configurable_serde::configure_serde;
use serde::{Serialize, Deserialize};
/// Defines a reusable configuration named `apply_api_config`:
macro_rules! apply_api_config {
($item:item) => {
#[configure_serde(
struct_rename_all = "camelCase",
enum_rename_all = "SCREAMING_SNAKE_CASE",
skip_if_none,
deny_unknown_fields
)]
$item
};
}
// Now, apply this configuration to a struct.
apply_api_config! {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Product {
product_id: String,
stock_count: Option<u32>,
}
}
// And to an enum.
apply_api_config! {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum Status {
InStock,
Backordered,
Discontinued,
}
}
// Test the struct serialization
let product = Product {
product_id: "prod-456".to_string(),
stock_count: Some(50)
};
let product_json = serde_json::to_string(&product).unwrap();
assert_eq!(product_json, r#"{"productId":"prod-456","stockCount":50}"#);
// Test the enum serialization
let status = Status::InStock;
let status_json = serde_json::to_string(&status).unwrap();
assert_eq!(status_json, r#""IN_STOCK""#);
This is the same as what you get using the create_config! macro, but it let's you to place the definition
in any place in your crate (that is: #[macro_export] works).
#[macro_export]
macro_rules! my_api_models {
($($item:item)*) => {
$(
#[configurable_serde::configure_serde(
struct_rename_all = "camelCase",
enum_rename_all = "SCREAMING_SNAKE_CASE",
skip_if_none
)]
$item
)*
};
}