Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions ipnetwork/src/ipv4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ impl<'de> serde::Deserialize<'de> for Ipv4Network {
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv4Network::from_str(&s).map_err(serde::de::Error::custom)
deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData))
}
}

Expand Down
3 changes: 1 addition & 2 deletions ipnetwork/src/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ impl<'de> serde::Deserialize<'de> for Ipv6Network {
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
Ipv6Network::from_str(&s).map_err(serde::de::Error::custom)
deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData))
}
}

Expand Down
4 changes: 2 additions & 2 deletions ipnetwork/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod error;
mod ipv4;
mod ipv6;
mod parse;
mod serde_helpers;
mod size;

pub use crate::{
Expand All @@ -42,8 +43,7 @@ impl<'de> serde::Deserialize<'de> for IpNetwork {
where
D: serde::Deserializer<'de>,
{
let s = <String>::deserialize(deserializer)?;
IpNetwork::from_str(&s).map_err(serde::de::Error::custom)
deserializer.deserialize_str(crate::serde_helpers::FromStrVisitor(core::marker::PhantomData))
}
}

Expand Down
36 changes: 36 additions & 0 deletions ipnetwork/src/serde_helpers.rs
Original file line number Diff line number Diff line change
@@ -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<F>(pub PhantomData<F>);

impl<'de, F> Visitor<'de> for FromStrVisitor<F>
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<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeError,
{
F::from_str(v).map_err(DeError::custom)
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: DeError,
{
F::from_str(v).map_err(DeError::custom)
}
}