Crate serde_untagged
source ·Expand description
This crate provides a Serde Visitor
implementation that is useful for
deserializing untagged enums.
Untagged enum Deserialize
impls look like this:
use serde::de::{Deserialize, Deserializer};
use serde_untagged::UntaggedEnumVisitor;
impl<'de> Deserialize<'de> for $MyType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
/*
*
*/
.deserialize(deserializer)
}
}
Inside the /* ... */
, we list each type that the untagged enum needs to
support deserializing from, giving a closure that turns the input into
$MyType. The following types are supported:
- bool
- i8, i16, i32, i64, i128, u8, u16, u32, u64, u128
- f32
- f64
- char
- string
- borrowed_str
- bytes
- borrowed_bytes
- byte_buf
- unit
- seq
- map
Example: string or struct
Cargo’s http.ssl-version
configuration supports deserialization from the
following two representations:
[http]
ssl-version = "tlsv1.3"
[http]
ssl-version.min = "tlsv1.2"
ssl-version.max = "tlsv1.3"
use serde::de::{Deserialize, Deserializer};
use serde_derive::Deserialize;
use serde_untagged::UntaggedEnumVisitor;
pub enum SslVersionConfig {
Single(String),
Range(SslVersionConfigRange),
}
impl<'de> Deserialize<'de> for SslVersionConfig {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.string(|single| Ok(SslVersionConfig::Single(single.to_owned())))
.map(|map| map.deserialize().map(SslVersionConfig::Range))
.deserialize(deserializer)
}
}
#[derive(Deserialize)]
pub struct SslVersionConfigRange {
pub min: Option<String>,
pub max: Option<String>,
}
Example: unit variant or bool
Cargo’s LTO setting in profiles supports the 5 values false
, true
,
"fat"
, "thin"
, and "off"
.
[profile.release]
lto = "thin"
use serde::de::{Deserialize, Deserializer, IntoDeserializer};
use serde_derive::Deserialize;
use serde_untagged::UntaggedEnumVisitor;
pub enum LinkTimeOptimization {
Enabled(bool),
Enum(LinkTimeOptimizationString),
}
impl<'de> Deserialize<'de> for LinkTimeOptimization {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.bool(|b| Ok(LinkTimeOptimization::Enabled(b)))
.string(|string| {
let de = string.into_deserializer();
LinkTimeOptimizationString::deserialize(de).map(LinkTimeOptimization::Enum)
})
.deserialize(deserializer)
}
}
#[derive(Deserialize)]
#[serde(rename = "lowercase")]
pub enum LinkTimeOptimizationString {
Fat,
Thin,
Off,
}
Since lto = true
means the same thing as lto = "fat"
to Cargo, there are
really only 4 distinct options. This type could be implemented alternatively
as:
use serde::de::{Deserialize, Deserializer, Unexpected};
use serde_untagged::UntaggedEnumVisitor;
pub enum LinkTimeOptimization {
ThinLocal, // false
Fat, // true or "fat"
Thin, // "thin"
Off, // "off"
}
impl<'de> Deserialize<'de> for LinkTimeOptimization {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.bool(|b| match b {
false => Ok(LinkTimeOptimization::ThinLocal),
true => Ok(LinkTimeOptimization::Fat),
})
.string(|string| match string {
"fat" => Ok(LinkTimeOptimization::Fat),
"thin" => Ok(LinkTimeOptimization::Thin),
"off" => Ok(LinkTimeOptimization::Off),
_ => Err(serde::de::Error::invalid_value(
Unexpected::Str(string),
&r#""fat" or "thin" or "off""#,
)),
})
.deserialize(deserializer)
}
}