Skip to content
Merged
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
45 changes: 42 additions & 3 deletions crates/uv-pypi-types/src/requirement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ pub enum RequirementError {
///
/// The main change is using [`RequirementSource`] to represent all supported package sources over
/// [`VersionOrUrl`], which collapses all URL sources into a single stringly type.
#[derive(
Hash, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, serde::Serialize, serde::Deserialize,
)]
#[derive(Debug, Clone, Ord, PartialOrd, serde::Serialize, serde::Deserialize)]
pub struct Requirement {
pub name: PackageName,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
Expand Down Expand Up @@ -97,6 +95,47 @@ impl Requirement {
}
}

impl std::hash::Hash for Requirement {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let Self {
name,
extras,
marker,
source,
origin: _,
} = self;
name.hash(state);
extras.hash(state);
marker.hash(state);
source.hash(state);
}
}

impl PartialEq for Requirement {
fn eq(&self, other: &Self) -> bool {
let Self {
name,
extras,
marker,
source,
origin: _,
} = self;
let Self {
name: other_name,
extras: other_extras,
marker: other_marker,
source: other_source,
origin: _,
} = other;
name == other_name
&& extras == other_extras
&& marker == other_marker
&& source == other_source
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need custom Ord/PartialOrd trait impls too, right? Otherwise, those will incorporate origin for comparisons and I think result in an inconsistency with your Eq/PartialEq trait impls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classic :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is truly classic. Thank you.


impl Eq for Requirement {}

impl From<Requirement> for uv_pep508::Requirement<VerbatimUrl> {
/// Convert a [`Requirement`] to a [`uv_pep508::Requirement`].
fn from(requirement: Requirement) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/it/tool_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,7 @@ fn tool_install_requirements_txt_arguments() {
----- stdout -----

----- stderr -----
Installed 2 executables: black, blackd
`black` is already installed
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an example: we didn't detect that this install request was identical to the previous, because in this latter case, the requirement came from a requirements.txt file rather than the CLI.

"###);

let requirements_txt = context.temp_dir.child("requirements.txt");
Expand Down