uv_distribution_types/
specified_requirement.rs1use std::borrow::Cow;
2use std::fmt::{Display, Formatter};
3
4use uv_git_types::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 marker: Option<MarkerTree>,
79 ) -> Self {
80 #[allow(clippy::manual_map)]
81 let git_reference = if let Some(rev) = rev {
82 Some(GitReference::from_rev(rev.to_string()))
83 } else if let Some(tag) = tag {
84 Some(GitReference::Tag(tag.to_string()))
85 } else if let Some(branch) = branch {
86 Some(GitReference::Branch(branch.to_string()))
87 } else {
88 None
89 };
90
91 match self {
92 Self::Named(mut requirement) => Self::Named(Requirement {
93 marker: marker
94 .map(|marker| {
95 requirement.marker.and(marker);
96 requirement.marker
97 })
98 .unwrap_or(requirement.marker),
99 source: match requirement.source {
100 RequirementSource::Git {
101 git,
102 subdirectory,
103 url,
104 } => {
105 let git = if let Some(git_reference) = git_reference {
106 git.with_reference(git_reference)
107 } else {
108 git
109 };
110 RequirementSource::Git {
111 git,
112 subdirectory,
113 url,
114 }
115 }
116 _ => requirement.source,
117 },
118 ..requirement
119 }),
120 Self::Unnamed(mut requirement) => Self::Unnamed(UnnamedRequirement {
121 marker: marker
122 .map(|marker| {
123 requirement.marker.and(marker);
124 requirement.marker
125 })
126 .unwrap_or(requirement.marker),
127 url: match requirement.url.parsed_url {
128 ParsedUrl::Git(mut git) => {
129 if let Some(git_reference) = git_reference {
130 git.url = git.url.with_reference(git_reference);
131 }
132 VerbatimParsedUrl {
133 parsed_url: ParsedUrl::Git(git),
134 verbatim: requirement.url.verbatim,
135 }
136 }
137 _ => requirement.url,
138 },
139 ..requirement
140 }),
141 }
142 }
143
144 pub fn extras(&self) -> &[ExtraName] {
146 match self {
147 Self::Named(requirement) => &requirement.extras,
148 Self::Unnamed(requirement) => &requirement.extras,
149 }
150 }
151
152 pub fn source(&self) -> Cow<'_, RequirementSource> {
154 match self {
155 Self::Named(requirement) => Cow::Borrowed(&requirement.source),
156 Self::Unnamed(requirement) => Cow::Owned(RequirementSource::from_parsed_url(
157 requirement.url.parsed_url.clone(),
158 requirement.url.verbatim.clone(),
159 )),
160 }
161 }
162
163 pub fn is_editable(&self) -> bool {
165 match self {
166 Self::Named(requirement) => requirement.is_editable(),
167 Self::Unnamed(requirement) => requirement.url.is_editable(),
168 }
169 }
170
171 pub fn hashes(&self) -> Option<Hashes> {
173 match self {
174 Self::Named(requirement) => requirement.hashes(),
175 Self::Unnamed(requirement) => {
176 let fragment = requirement.url.verbatim.fragment()?;
177 Hashes::parse_fragment(fragment).ok()
178 }
179 }
180 }
181}
182
183impl NameRequirementSpecification {
184 pub fn hashes(&self) -> Option<Hashes> {
186 let RequirementSource::Url { ref url, .. } = self.requirement.source else {
187 return None;
188 };
189 let fragment = url.fragment()?;
190 Hashes::parse_fragment(fragment).ok()
191 }
192}
193
194impl From<Requirement> for UnresolvedRequirementSpecification {
195 fn from(requirement: Requirement) -> Self {
196 Self {
197 requirement: UnresolvedRequirement::Named(requirement),
198 hashes: Vec::new(),
199 }
200 }
201}
202
203impl From<Requirement> for NameRequirementSpecification {
204 fn from(requirement: Requirement) -> Self {
205 Self {
206 requirement,
207 hashes: Vec::new(),
208 }
209 }
210}