diff --git a/crates/uv-configuration/src/required_version.rs b/crates/uv-configuration/src/required_version.rs index 339abb1cfdb4..256ec6791b75 100644 --- a/crates/uv-configuration/src/required_version.rs +++ b/crates/uv-configuration/src/required_version.rs @@ -1,3 +1,4 @@ +use std::fmt::Formatter; use std::str::FromStr; use uv_pep440::{Version, VersionSpecifier, VersionSpecifiers, VersionSpecifiersParseError}; @@ -49,8 +50,21 @@ impl schemars::JsonSchema for RequiredVersion { impl<'de> serde::Deserialize<'de> for RequiredVersion { fn deserialize>(deserializer: D) -> Result { - 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(self, v: &str) -> Result { + RequiredVersion::from_str(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-distribution-filename/src/wheel.rs b/crates/uv-distribution-filename/src/wheel.rs index 102778586ce2..699261194964 100644 --- a/crates/uv-distribution-filename/src/wheel.rs +++ b/crates/uv-distribution-filename/src/wheel.rs @@ -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(self, v: &str) -> Result { + WheelFilename::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-distribution-types/src/index_name.rs b/crates/uv-distribution-types/src/index_name.rs index 017a1ca0424f..ac4886294f4f 100644 --- a/crates/uv-distribution-types/src/index_name.rs +++ b/crates/uv-distribution-types/src/index_name.rs @@ -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) } } diff --git a/crates/uv-distribution-types/src/index_url.rs b/crates/uv-distribution-types/src/index_url.rs index d09d5d211a9d..dfffd03d41d2 100644 --- a/crates/uv-distribution-types/src/index_url.rs +++ b/crates/uv-distribution-types/src/index_url.rs @@ -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(self, v: &str) -> Result { + IndexUrl::from_str(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-git-types/src/oid.rs b/crates/uv-git-types/src/oid.rs index be417d4d0840..51ad40daa932 100644 --- a/crates/uv-git-types/src/oid.rs +++ b/crates/uv-git-types/src/oid.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt::{Display, Formatter}; use std::str::{self, FromStr}; use thiserror::Error; @@ -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(self, v: &str) -> Result { + GitOid::from_str(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-normalize/src/extra_name.rs b/crates/uv-normalize/src/extra_name.rs index 774c2a175e45..9e6b0da8504e 100644 --- a/crates/uv-normalize/src/extra_name.rs +++ b/crates/uv-normalize/src/extra_name.rs @@ -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(self, v: &str) -> Result { + ExtraName::from_str(v).map_err(serde::de::Error::custom) + } + + fn visit_string(self, v: String) -> Result { + ExtraName::from_owned(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-normalize/src/group_name.rs b/crates/uv-normalize/src/group_name.rs index d22ba814100b..45828132364e 100644 --- a/crates/uv-normalize/src/group_name.rs +++ b/crates/uv-normalize/src/group_name.rs @@ -1,5 +1,3 @@ -use std::fmt; -use std::fmt::{Display, Formatter}; use std::str::FromStr; use std::sync::LazyLock; @@ -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(self, v: &str) -> Result { + GroupName::from_str(v).map_err(serde::de::Error::custom) + } + + fn visit_string(self, v: String) -> Result { + GroupName::from_owned(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -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) } } diff --git a/crates/uv-normalize/src/package_name.rs b/crates/uv-normalize/src/package_name.rs index 41532ef7021e..c4c0cc22eaf9 100644 --- a/crates/uv-normalize/src/package_name.rs +++ b/crates/uv-normalize/src/package_name.rs @@ -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(self, v: &str) -> Result { + PackageName::from_str(v).map_err(serde::de::Error::custom) + } + + fn visit_string(self, v: String) -> Result { + PackageName::from_owned(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-pep440/src/version.rs b/crates/uv-pep440/src/version.rs index bfe16973d701..a48c350a43b4 100644 --- a/crates/uv-pep440/src/version.rs +++ b/crates/uv-pep440/src/version.rs @@ -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; @@ -725,14 +726,26 @@ impl Version { } } -/// impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result 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(self, v: &str) -> Result { + Version::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-pep440/src/version_specifier.rs b/crates/uv-pep440/src/version_specifier.rs index 4cbf6da748de..ecadb9dd374a 100644 --- a/crates/uv-pep440/src/version_specifier.rs +++ b/crates/uv-pep440/src/version_specifier.rs @@ -1,4 +1,5 @@ use std::cmp::Ordering; +use std::fmt::Formatter; use std::ops::Bound; use std::str::FromStr; @@ -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(self, v: &str) -> Result { + VersionSpecifiers::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -172,7 +186,7 @@ impl Serialize for VersionSpecifiers { where S: Serializer, { - serializer.collect_str( + serializer.serialize_str( &self .iter() .map(ToString::to_string) @@ -256,14 +270,26 @@ pub struct VersionSpecifier { pub(crate) version: Version, } -/// impl<'de> Deserialize<'de> for VersionSpecifier { fn deserialize(deserializer: D) -> Result 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(self, v: &str) -> Result { + VersionSpecifier::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-pep508/src/lib.rs b/crates/uv-pep508/src/lib.rs index c950482d6647..8b5de676434c 100644 --- a/crates/uv-pep508/src/lib.rs +++ b/crates/uv-pep508/src/lib.rs @@ -186,8 +186,24 @@ impl<'de, T: Pep508Url> Deserialize<'de> for Requirement { where D: Deserializer<'de>, { - let s = String::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(de::Error::custom) + struct RequirementVisitor(std::marker::PhantomData); + + impl serde::de::Visitor<'_> for RequirementVisitor { + type Value = Requirement; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string containing a PEP 508 requirement") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + FromStr::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(RequirementVisitor(std::marker::PhantomData)) } } diff --git a/crates/uv-pep508/src/marker/tree.rs b/crates/uv-pep508/src/marker/tree.rs index 447a77ee7aa4..edbcea60d92e 100644 --- a/crates/uv-pep508/src/marker/tree.rs +++ b/crates/uv-pep508/src/marker/tree.rs @@ -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(self, v: &str) -> Result { + StringVersion::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -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(self, v: &str) -> Result { + MarkerTree::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-pypi-types/src/lenient_requirement.rs b/crates/uv-pypi-types/src/lenient_requirement.rs index 8baaf96ed757..0537c2ca7e74 100644 --- a/crates/uv-pypi-types/src/lenient_requirement.rs +++ b/crates/uv-pypi-types/src/lenient_requirement.rs @@ -157,8 +157,21 @@ impl<'de> Deserialize<'de> for LenientVersionSpecifiers { 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 = LenientVersionSpecifiers; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + LenientVersionSpecifiers::from_str(v).map_err(de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-pypi-types/src/simple_json.rs b/crates/uv-pypi-types/src/simple_json.rs index ba8c0584a8a5..3ed17f7b594a 100644 --- a/crates/uv-pypi-types/src/simple_json.rs +++ b/crates/uv-pypi-types/src/simple_json.rs @@ -65,13 +65,37 @@ fn deserialize_version_specifiers_lenient<'de, D>( where D: Deserializer<'de>, { - let maybe_string: Option = Option::deserialize(deserializer)?; - let Some(string) = maybe_string else { - return Ok(None); - }; - Ok(Some( - LenientVersionSpecifiers::from_str(&string).map(VersionSpecifiers::from), - )) + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = Option>; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("a string representing a version specifier") + } + + fn visit_str(self, v: &str) -> Result { + Ok(Some( + LenientVersionSpecifiers::from_str(v).map(VersionSpecifiers::from), + )) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Visitor) + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(None) + } + } + + deserializer.deserialize_option(Visitor) } #[derive(Debug, Clone)] diff --git a/crates/uv-python/src/python_version.rs b/crates/uv-python/src/python_version.rs index b0a64d068839..f4c9abc4c766 100644 --- a/crates/uv-python/src/python_version.rs +++ b/crates/uv-python/src/python_version.rs @@ -63,8 +63,21 @@ impl schemars::JsonSchema for PythonVersion { impl<'de> serde::Deserialize<'de> for PythonVersion { fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - PythonVersion::from_str(&s).map_err(serde::de::Error::custom) + struct Visitor; + + impl serde::de::Visitor<'_> for Visitor { + type Value = PythonVersion; + + fn expecting(&self, f: &mut Formatter) -> std::fmt::Result { + f.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + PythonVersion::from_str(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-resolver/src/lock/mod.rs b/crates/uv-resolver/src/lock/mod.rs index a410e1f43deb..9c615c863949 100644 --- a/crates/uv-resolver/src/lock/mod.rs +++ b/crates/uv-resolver/src/lock/mod.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::convert::Infallible; use std::error::Error; -use std::fmt::{Debug, Display}; +use std::fmt::{Debug, Display, Formatter}; use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -4361,12 +4361,25 @@ impl Display for Hash { } impl<'de> serde::Deserialize<'de> for Hash { - fn deserialize(d: D) -> Result + fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { - let string = String::deserialize(d)?; - string.parse().map_err(serde::de::Error::custom) + struct Visitor; + + impl serde::de::Visitor<'_> for Visitor { + type Value = Hash; + + fn expecting(&self, f: &mut Formatter) -> std::fmt::Result { + f.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + Hash::from_str(v).map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-small-str/src/lib.rs b/crates/uv-small-str/src/lib.rs index 40324426fa62..3f503762490a 100644 --- a/crates/uv-small-str/src/lib.rs +++ b/crates/uv-small-str/src/lib.rs @@ -85,26 +85,21 @@ impl serde::Serialize for SmallString { impl<'de> serde::Deserialize<'de> for SmallString { fn deserialize>(deserializer: D) -> Result { - let s = deserializer.deserialize_str(SmallStringVisitor)?; - Ok(s) - } -} - -struct SmallStringVisitor; + struct Visitor; -impl serde::de::Visitor<'_> for SmallStringVisitor { - type Value = SmallString; + impl serde::de::Visitor<'_> for Visitor { + type Value = SmallString; - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("a string") - } + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("a string") + } - fn visit_str(self, v: &str) -> Result { - Ok(v.into()) - } + fn visit_str(self, v: &str) -> Result { + Ok(v.into()) + } + } - fn visit_string(self, v: String) -> Result { - Ok(v.into()) + deserializer.deserialize_str(Visitor) } } diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index 758d10d481a6..173fad18d8e2 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -7,6 +7,7 @@ //! Then lowers them into a dependency specification. use std::collections::BTreeMap; +use std::fmt::Formatter; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -713,8 +714,39 @@ pub struct ToolUvWorkspace { } /// (De)serialize globs as strings. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct SerdePattern(#[serde(with = "serde_from_and_to_string")] pub Pattern); +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SerdePattern(Pattern); + +impl serde::ser::Serialize for SerdePattern { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + self.0.as_str().serialize(serializer) + } +} + +impl<'de> serde::Deserialize<'de> for SerdePattern { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl serde::de::Visitor<'_> for Visitor { + type Value = SerdePattern; + + fn expecting(&self, f: &mut Formatter) -> std::fmt::Result { + f.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result { + Pattern::from_str(v) + .map(SerdePattern) + .map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(Visitor) + } +} #[cfg(feature = "schemars")] impl schemars::JsonSchema for SerdePattern { @@ -1585,30 +1617,3 @@ pub enum DependencyType { /// A dependency in `dependency-groups.{0}`. Group(GroupName), } - -/// -mod serde_from_and_to_string { - use std::fmt::Display; - use std::str::FromStr; - - use serde::{de, Deserialize, Deserializer, Serializer}; - - pub(super) fn serialize(value: &T, serializer: S) -> Result - where - T: Display, - S: Serializer, - { - serializer.collect_str(value) - } - - pub(super) fn deserialize<'de, T, D>(deserializer: D) -> Result - where - T: FromStr, - T::Err: Display, - D: Deserializer<'de>, - { - String::deserialize(deserializer)? - .parse() - .map_err(de::Error::custom) - } -} diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 3066e8ed5c2e..b8147b8c3694 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -4131,23 +4131,22 @@ fn override_dependency_from_workspace_invalid_syntax() -> Result<()> { ----- stderr ----- warning: Failed to parse `pyproject.toml` during settings discovery: - TOML parse error at line 9, column 29 - | - 9 | override-dependencies = [ - | ^ + TOML parse error at line 10, column 7 + | + 10 | "werkzeug=2.3.0" + | ^^^^^^^^^^^^^^^^ no such comparison operator "=", must be one of ~= == != <= >= < > === werkzeug=2.3.0 ^^^^^^ error: Failed to parse: `pyproject.toml` - Caused by: TOML parse error at line 9, column 29 - | - 9 | override-dependencies = [ - | ^ + Caused by: TOML parse error at line 10, column 7 + | + 10 | "werkzeug=2.3.0" + | ^^^^^^^^^^^^^^^^ no such comparison operator "=", must be one of ~= == != <= >= < > === werkzeug=2.3.0 ^^^^^^ - "### );