use serde::{Deserialize, Serialize, Deserializer};
use serde::ser::Serializer;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dns {
pub zone: Option<String>
}
pub fn is_dns_none(dns: &Option<Dns>) -> bool {
match dns {
None => true,
Some(dns) => dns.zone.is_none(),
}
}
impl Serialize for Dns {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match &self.zone {
Some(zone) => serializer.serialize_str(zone),
None => serializer.serialize_none()
}
}
}
impl<'de> Deserialize<'de> for Dns {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = serde::de::DeserializeSeed::deserialize(
StringOrStruct::default(),
deserializer
)?;
Ok(value)
}
}
#[derive(Default)]
struct StringOrStruct {
_private: ()
}
impl<'de> serde::de::DeserializeSeed<'de> for StringOrStruct {
type Value = Dns;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct StringVisitor;
impl<'de> serde::de::Visitor<'de> for StringVisitor {
type Value = Dns;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("string or dns object")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Dns { zone: Some(value.to_string()) })
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Dns { zone: Some(value) })
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Dns { zone: None })
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer)
}
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Dns { zone: None })
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut zone = None;
while let Some(key) = map.next_key::<String>()? {
if key == "zone" {
zone = map.next_value()?;
} else {
map.next_value::<serde::de::IgnoredAny>()?;
}
}
Ok(Dns { zone })
}
}
deserializer.deserialize_any(StringVisitor)
}
}
impl Default for Dns {
fn default() -> Self {
Self { zone: None }
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_dns_serialization() {
let dns = Dns {
zone: Some("example.com".to_string()),
};
let json = serde_json::to_string(&dns).unwrap();
assert_eq!(json, r#""example.com""#);
let dns = Dns { zone: None };
let json = serde_json::to_string(&dns).unwrap();
assert_eq!(json, "null");
let dns = Dns::default();
let json = serde_json::to_string(&dns).unwrap();
assert_eq!(json, "null");
}
#[test]
fn test_dns_in_struct() {
#[derive(Serialize)]
struct Container {
#[serde(skip_serializing_if = "is_dns_none")]
dns: Option<Dns>,
other: String,
}
let container = Container {
dns: Some(Dns {
zone: Some("example.com".to_string()),
}),
other: "test".to_string(),
};
let json = serde_json::to_string(&container).unwrap();
assert_eq!(json, r#"{"dns":"example.com","other":"test"}"#);
let container = Container {
dns: Some(Dns { zone: None }),
other: "test".to_string(),
};
let json = serde_json::to_string(&container).unwrap();
assert_eq!(json, r#"{"other":"test"}"#);
let container = Container {
dns: None,
other: "test".to_string(),
};
let json = serde_json::to_string(&container).unwrap();
assert_eq!(json, r#"{"other":"test"}"#);
}
#[test]
fn test_dns_deserialization() {
let dns: Dns = serde_json::from_str(r#""example.com""#).unwrap();
assert_eq!(dns.zone, Some("example.com".to_string()));
let dns: Dns = serde_json::from_str("null").unwrap();
assert_eq!(dns.zone, None);
let dns: Dns = serde_json::from_str("{}").unwrap();
assert_eq!(dns.zone, None);
let dns: Dns = serde_json::from_str(r#"{"zone":null}"#).unwrap();
assert_eq!(dns.zone, None);
let dns: Dns = serde_json::from_str(r#"{"zone":"example.com"}"#).unwrap();
assert_eq!(dns.zone, Some("example.com".to_string()));
}
}