| Crates.io | dispatch_macros |
| lib.rs | dispatch_macros |
| version | 0.1.3 |
| created_at | 2025-07-24 15:06:26.671572+00 |
| updated_at | 2025-07-26 10:36:07.947156+00 |
| description | Type-safe, declarative dispatch maps for Rust configuration with automatic glue and zero boilerplate. |
| homepage | https://github.com/BppleMan/dispatch_map |
| repository | https://github.com/BppleMan/dispatch_map |
| max_upload_size | |
| id | 1766171 |
| size | 13,025 |
💡 Polymorphic Map dispatch, declarative deserialization, type safety, business structure and configuration perfectly aligned!
You are developing an aggregate payment platform, and each payment channel has its own unique Rust config type, for example:
enum PaymentChannel {
Stripe,
AliPay,
PayPal,
}
struct StripeConfig {
api_key: String,
region: String
}
struct AliPayConfig {
app_id: String,
private_key: String
}
struct PayPalConfig {
client_id: String,
client_secret: String
}
enum ChannelConfig {
Stripe(StripeConfig),
AliPay(AliPayConfig),
PayPal(PayPalConfig),
}
struct AppConfig {
channels: HashMap<PaymentChannel, ChannelConfig>
}
You want to configure these structures in .toml/.yaml/.json files, achieving "type-driven, clear structure".
When using #[derive(Serialize, Deserialize)] directly, you expect to get:
[channels.Stripe]
api_key = "sk_test_123"
region = "us"
But what you actually get is:
[channels.Stripe.Stripe]
api_key = "sk_test_123"
region = "us"
Each enum layer adds an extra nesting!
Maintaining this is extremely redundant, and deserialization becomes mechanical and repetitive.
You have to write such "deserialization glue":
fn deserialize_channels<'de, D>(deserializer: D) -> Result<HashMap<PaymentChannel, ChannelConfig>, D::Error>
where
D: serde::Deserializer<'de>,
{
let table = toml::Value::deserialize(deserializer)?;
let map = table.as_table().ok_or_else(|| de::Error::custom("channels should be a table"))?;
let mut result = HashMap::new();
for (key, value) in map {
let channel: PaymentChannel = key.parse().map_err(de::Error::custom)?;
let config = match channel {
PaymentChannel::Stripe => ChannelConfig::Stripe(StripeConfig::deserialize(value)?),
PaymentChannel::AliPay => ChannelConfig::AliPay(AliPayConfig::deserialize(value)?),
PaymentChannel::PayPal => ChannelConfig::PayPal(PayPalConfig::deserialize(value)?),
};
result.insert(channel, config);
}
Ok(result)
}
You can add one line to ChannelConfig:
#[serde(untagged)]
enum ChannelConfig {
Stripe(StripeConfig),
AliPay(AliPayConfig),
PayPal(PayPalConfig),
}
This lets serde automatically unbox, leaving only one layer of nesting and a clean config structure.
With dispatch_map, you can declare glue in one line, all type dispatch is automatic, no need to write tedious match by hand.
use dispatch_map::{DispatchMap, DispatchSeed};
use dispatch_map_derive::{DispatchKey, dispatch_pattern};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, DispatchKey)]
pub enum PaymentChannel {
Stripe,
AliPay,
PayPal,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ChannelConfig {
Stripe(StripeConfig),
AliPay(AliPayConfig),
PayPal(PayPalConfig),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StripeConfig {
pub api_key: String,
pub region: String,
}
// Other channels similar...
dispatch_pattern! {
PaymentChannel::Stripe => ChannelConfig::Stripe(StripeConfig),
PaymentChannel::AliPay => ChannelConfig::AliPay(AliPayConfig),
PaymentChannel::PayPal => ChannelConfig::PayPal(PayPalConfig),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AppConfig {
pub channels: DispatchMap<PaymentChannel, ChannelConfig>,
}
let src = r#"
[channels.Stripe]
api_key = "sk_test_123"
region = "us"
[channels.AliPay]
app_id = "2023"
private_key = "..."
"#;
let cfg: AppConfig = toml::from_str(src).unwrap();
assert!(matches!(cfg.channels.get(&PaymentChannel::Stripe), Some(ChannelConfig::Stripe(_))));
Eq + Hash + Deserialize + Serialize, and the value can be glued, it works.dispatch_map makes polymorphic config serialization/deserialization simple and type-safe, declaration is glue, focus on business design, config experience as you wish!
Welcome star, PR, and questions! If you find it useful, don't forget to leave a like on crates.io ⭐️!