Skip to content

Commit

Permalink
Avoid using owned String in deserializers (#11764)
Browse files Browse the repository at this point in the history
## Summary

This is the pattern I see in a variety of crates, and I believe this is
preferred if you don't _need_ an owned `String`, since you can avoid the
allocation. This could be pretty impactful for us?
  • Loading branch information
charliermarsh authored Feb 25, 2025
1 parent 275db06 commit c37af94
Show file tree
Hide file tree
Showing 19 changed files with 350 additions and 104 deletions.
18 changes: 16 additions & 2 deletions crates/uv-configuration/src/required_version.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt::Formatter;
use std::str::FromStr;

use uv_pep440::{Version, VersionSpecifier, VersionSpecifiers, VersionSpecifiersParseError};
Expand Down Expand Up @@ -49,8 +50,21 @@ impl schemars::JsonSchema for RequiredVersion {

impl<'de> serde::Deserialize<'de> for RequiredVersion {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = RequiredVersion;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
RequiredVersion::from_str(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
17 changes: 15 additions & 2 deletions crates/uv-distribution-filename/src/wheel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,21 @@ impl<'de> Deserialize<'de> for WheelFilename {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = WheelFilename;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
WheelFilename::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/uv-distribution-types/src/index_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ impl<'de> serde::de::Deserialize<'de> for IndexName {
where
D: serde::de::Deserializer<'de>,
{
IndexName::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
let s = String::deserialize(deserializer)?;
IndexName::new(s).map_err(serde::de::Error::custom)
}
}

Expand Down
17 changes: 15 additions & 2 deletions crates/uv-distribution-types/src/index_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,21 @@ impl<'de> serde::de::Deserialize<'de> for IndexUrl {
where
D: serde::de::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
IndexUrl::from_str(&s).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = IndexUrl;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
IndexUrl::from_str(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
19 changes: 16 additions & 3 deletions crates/uv-git-types/src/oid.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fmt::Display;
use std::fmt::{Display, Formatter};
use std::str::{self, FromStr};

use thiserror::Error;
Expand Down Expand Up @@ -74,8 +74,21 @@ impl<'de> serde::Deserialize<'de> for GitOid {
where
D: serde::Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
GitOid::from_str(&value).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = GitOid;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
GitOid::from_str(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
21 changes: 19 additions & 2 deletions crates/uv-normalize/src/extra_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,25 @@ impl<'de> Deserialize<'de> for ExtraName {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = ExtraName;

fn expecting(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
ExtraName::from_str(v).map_err(serde::de::Error::custom)
}

fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
ExtraName::from_owned(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
27 changes: 21 additions & 6 deletions crates/uv-normalize/src/group_name.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use std::sync::LazyLock;

Expand Down Expand Up @@ -41,8 +39,25 @@ impl<'de> Deserialize<'de> for GroupName {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = GroupName;

fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
GroupName::from_str(v).map_err(serde::de::Error::custom)
}

fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
GroupName::from_owned(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand All @@ -55,8 +70,8 @@ impl Serialize for GroupName {
}
}

impl Display for GroupName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
impl std::fmt::Display for GroupName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
Expand Down
21 changes: 19 additions & 2 deletions crates/uv-normalize/src/package_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,25 @@ impl<'de> Deserialize<'de> for PackageName {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
struct Visitor;

impl serde::de::Visitor<'_> for Visitor {
type Value = PackageName;

fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
PackageName::from_str(v).map_err(serde::de::Error::custom)
}

fn visit_string<E: serde::de::Error>(self, v: String) -> Result<Self::Value, E> {
PackageName::from_owned(v).map_err(serde::de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
19 changes: 16 additions & 3 deletions crates/uv-pep440/src/version.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::Formatter;
use std::num::NonZero;
use std::ops::Deref;
use std::sync::LazyLock;
Expand Down Expand Up @@ -725,14 +726,26 @@ impl Version {
}
}

/// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
impl<'de> Deserialize<'de> for Version {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = Version;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
Version::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
38 changes: 32 additions & 6 deletions crates/uv-pep440/src/version_specifier.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::cmp::Ordering;
use std::fmt::Formatter;
use std::ops::Bound;
use std::str::FromStr;

Expand Down Expand Up @@ -161,8 +162,21 @@ impl<'de> Deserialize<'de> for VersionSpecifiers {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = VersionSpecifiers;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
VersionSpecifiers::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand All @@ -172,7 +186,7 @@ impl Serialize for VersionSpecifiers {
where
S: Serializer,
{
serializer.collect_str(
serializer.serialize_str(
&self
.iter()
.map(ToString::to_string)
Expand Down Expand Up @@ -256,14 +270,26 @@ pub struct VersionSpecifier {
pub(crate) version: Version,
}

/// <https://github.com/serde-rs/serde/issues/1316#issue-332908452>
impl<'de> Deserialize<'de> for VersionSpecifier {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = VersionSpecifier;

fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
VersionSpecifier::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
20 changes: 18 additions & 2 deletions crates/uv-pep508/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,24 @@ impl<'de, T: Pep508Url> Deserialize<'de> for Requirement<T> {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
struct RequirementVisitor<T>(std::marker::PhantomData<T>);

impl<T: Pep508Url> serde::de::Visitor<'_> for RequirementVisitor<T> {
type Value = Requirement<T>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string containing a PEP 508 requirement")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
FromStr::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(RequirementVisitor(std::marker::PhantomData))
}
}

Expand Down
34 changes: 30 additions & 4 deletions crates/uv-pep508/src/marker/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,21 @@ impl<'de> Deserialize<'de> for StringVersion {
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Self::from_str(&string).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = StringVersion;

fn expecting(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
StringVersion::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down Expand Up @@ -651,8 +664,21 @@ impl<'de> Deserialize<'de> for MarkerTree {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
struct Visitor;

impl de::Visitor<'_> for Visitor {
type Value = MarkerTree;

fn expecting(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("a string")
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
MarkerTree::from_str(v).map_err(de::Error::custom)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand Down
Loading

0 comments on commit c37af94

Please sign in to comment.