use serde::Deserialize;
use serde::de::{self, Deserializer};
pub(crate) fn lossy_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Wire {
Num(i64),
Str(String),
}
match Wire::deserialize(deserializer)? {
Wire::Num(n) => Ok(n),
Wire::Str(s) => s.trim().parse::<i64>().map_err(de::Error::custom),
}
}
pub(crate) fn lossy_opt_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Wire {
Num(u64),
Str(String),
None,
}
match Option::<Wire>::deserialize(deserializer)? {
Some(Wire::Num(n)) => Ok(Some(n)),
Some(Wire::Str(s)) => {
let trimmed = s.trim();
if trimmed.is_empty() {
Ok(None)
} else {
trimmed.parse::<u64>().map(Some).map_err(de::Error::custom)
}
}
Some(Wire::None) | None => Ok(None),
}
}
pub(crate) fn lossy_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Wire {
Num(f64),
Str(String),
}
match Wire::deserialize(deserializer)? {
Wire::Num(n) => Ok(n),
Wire::Str(s) => s.trim().parse::<f64>().map_err(de::Error::custom),
}
}
pub(crate) fn lossy_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Wire {
Bool(bool),
Num(i64),
Str(String),
}
match Wire::deserialize(deserializer)? {
Wire::Bool(b) => Ok(b),
Wire::Num(n) => Ok(n != 0),
Wire::Str(s) => match s.trim().to_ascii_lowercase().as_str() {
"true" | "1" | "yes" => Ok(true),
"false" | "0" | "no" | "" => Ok(false),
other => Err(de::Error::custom(format!(
"invalid boolean string: {other}"
))),
},
}
}