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 #[allow(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::Git {
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::Git {
117 git,
118 subdirectory,
119 url,
120 }
121 }
122 _ => requirement.source,
123 },
124 ..requirement
125 }),
126 Self::Unnamed(mut requirement) => Self::Unnamed(UnnamedRequirement {
127 marker: marker
128 .map(|marker| {
129 requirement.marker.and(marker);
130 requirement.marker
131 })
132 .unwrap_or(requirement.marker),
133 url: match requirement.url.parsed_url {
134 ParsedUrl::Git(mut git) => {
135 if let Some(git_reference) = git_reference {
136 git.url = git.url.with_reference(git_reference);
137 }
138 if let Some(lfs) = lfs {
139 git.url = git.url.with_lfs(GitLfs::from(lfs));
140 }
141 VerbatimParsedUrl {
142 parsed_url: ParsedUrl::Git(git),
143 verbatim: requirement.url.verbatim,
144 }
145 }
146 _ => requirement.url,
147 },
148 ..requirement
149 }),
150 }
151 }
152
153 pub fn extras(&self) -> &[ExtraName] {
155 match self {
156 Self::Named(requirement) => &requirement.extras,
157 Self::Unnamed(requirement) => &requirement.extras,
158 }
159 }
160
161 pub fn source(&self) -> Cow<'_, RequirementSource> {
163 match self {
164 Self::Named(requirement) => Cow::Borrowed(&requirement.source),
165 Self::Unnamed(requirement) => Cow::Owned(RequirementSource::from_parsed_url(
166 requirement.url.parsed_url.clone(),
167 requirement.url.verbatim.clone(),
168 )),
169 }
170 }
171
172 pub fn is_editable(&self) -> bool {
174 match self {
175 Self::Named(requirement) => requirement.is_editable(),
176 Self::Unnamed(requirement) => requirement.url.is_editable(),
177 }
178 }
179
180 pub fn hashes(&self) -> Option<Hashes> {
182 match self {
183 Self::Named(requirement) => requirement.hashes(),
184 Self::Unnamed(requirement) => {
185 let fragment = requirement.url.verbatim.fragment()?;
186 Hashes::parse_fragment(fragment).ok()
187 }
188 }
189 }
190}
191
192impl NameRequirementSpecification {
193 pub fn hashes(&self) -> Option<Hashes> {
195 let RequirementSource::Url { ref url, .. } = self.requirement.source else {
196 return None;
197 };
198 let fragment = url.fragment()?;
199 Hashes::parse_fragment(fragment).ok()
200 }
201}
202
203impl From<Requirement> for UnresolvedRequirementSpecification {
204 fn from(requirement: Requirement) -> Self {
205 Self {
206 requirement: UnresolvedRequirement::Named(requirement),
207 hashes: Vec::new(),
208 }
209 }
210}
211
212impl From<Requirement> for NameRequirementSpecification {
213 fn from(requirement: Requirement) -> Self {
214 Self {
215 requirement,
216 hashes: Vec::new(),
217 }
218 }
219}