uv_distribution_types/
specified_requirement.rs1use std::borrow::Cow;
2use std::fmt::{Display, Formatter};
3
4use uv_git_types::{GitLfs, GitReference};
5use uv_normalize::ExtraName;
6use uv_pep508::{MarkerEnvironment, MarkerTree, UnnamedRequirement};
7use uv_pypi_types::{Hashes, ParsedUrl};
8
9use crate::{Requirement, RequirementSource, VerbatimParsedUrl};
10
11#[derive(Debug, Clone, Eq, PartialEq, Hash)]
14pub struct NameRequirementSpecification {
15 pub requirement: Requirement,
17 pub hashes: Vec<String>,
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash)]
24pub struct UnresolvedRequirementSpecification {
25 pub requirement: UnresolvedRequirement,
27 pub hashes: Vec<String>,
29}
30
31#[derive(Hash, Debug, Clone, Eq, PartialEq)]
39pub enum UnresolvedRequirement {
40 Named(Requirement),
43 Unnamed(UnnamedRequirement<VerbatimParsedUrl>),
45}
46
47impl Display for UnresolvedRequirement {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Self::Named(requirement) => write!(f, "{requirement}"),
51 Self::Unnamed(requirement) => write!(f, "{requirement}"),
52 }
53 }
54}
55
56impl UnresolvedRequirement {
57 pub fn evaluate_markers(&self, env: Option<&MarkerEnvironment>, extras: &[ExtraName]) -> bool {
64 match self {
65 Self::Named(requirement) => requirement.evaluate_markers(env, extras),
66 Self::Unnamed(requirement) => requirement.evaluate_optional_environment(env, extras),
67 }
68 }
69
70 #[must_use]
73 pub fn augment_requirement(
74 self,
75 rev: Option<&str>,
76 tag: Option<&str>,
77 branch: Option<&str>,
78 lfs: Option<bool>,
79 marker: Option<MarkerTree>,
80 ) -> Self {
81 #[expect(clippy::manual_map)]
82 let git_reference = if let Some(rev) = rev {
83 Some(GitReference::from_rev(rev.to_string()))
84 } else if let Some(tag) = tag {
85 Some(GitReference::Tag(tag.to_string()))
86 } else if let Some(branch) = branch {
87 Some(GitReference::Branch(branch.to_string()))
88 } else {
89 None
90 };
91
92 match self {
93 Self::Named(mut requirement) => Self::Named(Requirement {
94 marker: marker
95 .map(|marker| {
96 requirement.marker.and(marker);
97 requirement.marker
98 })
99 .unwrap_or(requirement.marker),
100 source: match requirement.source {
101 RequirementSource::GitDirectory {
102 git,
103 subdirectory,
104 url,
105 } => {
106 let git = if let Some(git_reference) = git_reference {
107 git.with_reference(git_reference)
108 } else {
109 git
110 };
111 let git = if let Some(lfs) = lfs {
112 git.with_lfs(GitLfs::from(lfs))
113 } else {
114 git
115 };
116 RequirementSource::GitDirectory {
117 git,
118 subdirectory,
119 url,
120 }
121 }
122 RequirementSource::GitPath {
123 git,
124 install_path,
125 ext,
126 url,
127 } => {
128 let git = if let Some(git_reference) = git_reference {
129 git.with_reference(git_reference)
130 } else {
131 git
132 };
133 let git = if let Some(lfs) = lfs {
134 git.with_lfs(GitLfs::from(lfs))
135 } else {
136 git
137 };
138 RequirementSource::GitPath {
139 git,
140 install_path,
141 ext,
142 url,
143 }
144 }
145 _ => requirement.source,
146 },
147 ..requirement
148 }),
149 Self::Unnamed(mut requirement) => Self::Unnamed(UnnamedRequirement {
150 marker: marker
151 .map(|marker| {
152 requirement.marker.and(marker);
153 requirement.marker
154 })
155 .unwrap_or(requirement.marker),
156 url: match requirement.url.parsed_url {
157 ParsedUrl::GitDirectory(mut git) => {
158 if let Some(git_reference) = git_reference {
159 git.url = git.url.with_reference(git_reference);
160 }
161 if let Some(lfs) = lfs {
162 git.url = git.url.with_lfs(GitLfs::from(lfs));
163 }
164 VerbatimParsedUrl {
165 parsed_url: ParsedUrl::GitDirectory(git),
166 verbatim: requirement.url.verbatim,
167 }
168 }
169 ParsedUrl::GitPath(mut git) => {
170 if let Some(git_reference) = git_reference {
171 git.url = git.url.with_reference(git_reference);
172 }
173 if let Some(lfs) = lfs {
174 git.url = git.url.with_lfs(GitLfs::from(lfs));
175 }
176 VerbatimParsedUrl {
177 parsed_url: ParsedUrl::GitPath(git),
178 verbatim: requirement.url.verbatim,
179 }
180 }
181 _ => requirement.url,
182 },
183 ..requirement
184 }),
185 }
186 }
187
188 pub fn source(&self) -> Cow<'_, RequirementSource> {
190 match self {
191 Self::Named(requirement) => Cow::Borrowed(&requirement.source),
192 Self::Unnamed(requirement) => Cow::Owned(RequirementSource::from_parsed_url(
193 requirement.url.parsed_url.clone(),
194 requirement.url.verbatim.clone(),
195 )),
196 }
197 }
198
199 pub fn hashes(&self) -> Option<Hashes> {
201 match self {
202 Self::Named(requirement) => requirement.hashes(),
203 Self::Unnamed(requirement) => {
204 let fragment = requirement.url.verbatim.fragment()?;
205 Hashes::parse_fragment(fragment).ok()
206 }
207 }
208 }
209}
210
211impl From<Requirement> for UnresolvedRequirementSpecification {
212 fn from(requirement: Requirement) -> Self {
213 Self {
214 requirement: UnresolvedRequirement::Named(requirement),
215 hashes: Vec::new(),
216 }
217 }
218}
219
220impl From<Requirement> for NameRequirementSpecification {
221 fn from(requirement: Requirement) -> Self {
222 Self {
223 requirement,
224 hashes: Vec::new(),
225 }
226 }
227}