diff --git a/ipnetwork/src/ipv4.rs b/ipnetwork/src/ipv4.rs index cfcd070..31e7b8b 100644 --- a/ipnetwork/src/ipv4.rs +++ b/ipnetwork/src/ipv4.rs @@ -20,8 +20,7 @@ impl<'de> serde::Deserialize<'de> for Ipv4Network { where D: serde::Deserializer<'de>, { - let s = ::deserialize(deserializer)?; - Ipv4Network::from_str(&s).map_err(serde::de::Error::custom) + deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData)) } } diff --git a/ipnetwork/src/ipv6.rs b/ipnetwork/src/ipv6.rs index 36c9daa..9abe4ed 100644 --- a/ipnetwork/src/ipv6.rs +++ b/ipnetwork/src/ipv6.rs @@ -21,8 +21,7 @@ impl<'de> serde::Deserialize<'de> for Ipv6Network { where D: serde::Deserializer<'de>, { - let s = ::deserialize(deserializer)?; - Ipv6Network::from_str(&s).map_err(serde::de::Error::custom) + deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData)) } } diff --git a/ipnetwork/src/lib.rs b/ipnetwork/src/lib.rs index f46c463..a354cd6 100644 --- a/ipnetwork/src/lib.rs +++ b/ipnetwork/src/lib.rs @@ -19,6 +19,7 @@ mod error; mod ipv4; mod ipv6; mod parse; +mod serde_helpers; mod size; pub use crate::{ @@ -42,8 +43,7 @@ impl<'de> serde::Deserialize<'de> for IpNetwork { where D: serde::Deserializer<'de>, { - let s = ::deserialize(deserializer)?; - IpNetwork::from_str(&s).map_err(serde::de::Error::custom) + deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData)) } } diff --git a/ipnetwork/src/serde_helpers.rs b/ipnetwork/src/serde_helpers.rs new file mode 100644 index 0000000..ffc144c --- /dev/null +++ b/ipnetwork/src/serde_helpers.rs @@ -0,0 +1,36 @@ +#![cfg(feature = "serde")] + +//! Useful [`serde`] helpers. + +use core::{fmt, marker::PhantomData, str::FromStr}; + +use serde::de::{Error as DeError, Visitor}; + +/// Deserialize a struct implementing [`FromStr`] without allocating a temporary String. +pub(crate) struct FromStrVisitor(pub PhantomData); + +impl<'de, F> Visitor<'de> for FromStrVisitor +where + F: FromStr, + F::Err: fmt::Display, +{ + type Value = F; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a network as a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: DeError, + { + F::from_str(v).map_err(DeError::custom) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: DeError, + { + F::from_str(v).map_err(DeError::custom) + } +}