1use std::path::PathBuf;
2use std::{collections::BTreeMap, num::NonZeroUsize};
3
4use url::Url;
5
6use uv_configuration::{
7 BuildIsolation, ExportFormat, IndexStrategy, KeyringProviderType, NoSources, ProxyUrl,
8 Reinstall, RequiredVersion, TargetTriple, TrustedPublishing, Upgrade,
9};
10use uv_distribution_types::{
11 ConfigSettings, ExtraBuildVariables, Index, IndexUrl, PackageConfigSettings, PipExtraIndex,
12 PipFindLinks, PipIndex,
13};
14use uv_install_wheel::LinkMode;
15use uv_pypi_types::{SchemaConflicts, SupportedEnvironments};
16use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
17use uv_redacted::DisplaySafeUrl;
18use uv_resolver::{
19 AnnotationStyle, ExcludeNewer, ExcludeNewerPackage, ExcludeNewerValue, ForkStrategy,
20 PrereleaseMode, ResolutionMode,
21};
22use uv_torch::TorchMode;
23use uv_workspace::pyproject::ExtraBuildDependencies;
24use uv_workspace::pyproject_mut::AddBoundsKind;
25
26use crate::{FilesystemOptions, Options, PipOptions};
27
28pub trait Combine {
29 #[must_use]
41 fn combine(self, other: Self) -> Self;
42}
43
44impl Combine for Option<FilesystemOptions> {
45 fn combine(self, other: Self) -> Self {
47 match (self, other) {
48 (Some(a), Some(b)) => Some(FilesystemOptions(
49 a.into_options().combine(b.into_options()),
50 )),
51 (a, b) => a.or(b),
52 }
53 }
54}
55
56impl Combine for Option<Options> {
57 fn combine(self, other: Self) -> Self {
59 match (self, other) {
60 (Some(a), Some(b)) => Some(a.combine(b)),
61 (a, b) => a.or(b),
62 }
63 }
64}
65
66impl Combine for Option<PipOptions> {
67 fn combine(self, other: Self) -> Self {
68 match (self, other) {
69 (Some(a), Some(b)) => Some(a.combine(b)),
70 (a, b) => a.or(b),
71 }
72 }
73}
74
75macro_rules! impl_combine_or {
76 ($name:ident) => {
77 impl Combine for Option<$name> {
78 fn combine(self, other: Option<$name>) -> Option<$name> {
79 self.or(other)
80 }
81 }
82 };
83}
84
85impl_combine_or!(AddBoundsKind);
86impl_combine_or!(AnnotationStyle);
87impl_combine_or!(ExcludeNewer);
88impl_combine_or!(ExcludeNewerValue);
89impl_combine_or!(ExportFormat);
90impl_combine_or!(ForkStrategy);
91impl_combine_or!(Index);
92impl_combine_or!(IndexStrategy);
93impl_combine_or!(IndexUrl);
94impl_combine_or!(KeyringProviderType);
95impl_combine_or!(LinkMode);
96impl_combine_or!(DisplaySafeUrl);
97impl_combine_or!(NonZeroUsize);
98impl_combine_or!(PathBuf);
99impl_combine_or!(PipExtraIndex);
100impl_combine_or!(PipFindLinks);
101impl_combine_or!(PipIndex);
102impl_combine_or!(PrereleaseMode);
103impl_combine_or!(ProxyUrl);
104impl_combine_or!(PythonDownloads);
105impl_combine_or!(PythonPreference);
106impl_combine_or!(PythonVersion);
107impl_combine_or!(RequiredVersion);
108impl_combine_or!(ResolutionMode);
109impl_combine_or!(SchemaConflicts);
110impl_combine_or!(String);
111impl_combine_or!(SupportedEnvironments);
112impl_combine_or!(TargetTriple);
113impl_combine_or!(TorchMode);
114impl_combine_or!(TrustedPublishing);
115impl_combine_or!(Url);
116impl_combine_or!(bool);
117
118impl<T> Combine for Option<Vec<T>> {
119 fn combine(self, other: Self) -> Self {
122 match (self, other) {
123 (Some(mut a), Some(b)) => {
124 a.extend(b);
125 Some(a)
126 }
127 (a, b) => a.or(b),
128 }
129 }
130}
131
132impl<K: Ord, T> Combine for Option<BTreeMap<K, Vec<T>>> {
133 fn combine(self, other: Self) -> Self {
135 match (self, other) {
136 (Some(mut a), Some(b)) => {
137 for (key, value) in b {
138 a.entry(key).or_default().extend(value);
139 }
140 Some(a)
141 }
142 (a, b) => a.or(b),
143 }
144 }
145}
146
147impl Combine for Option<ExcludeNewerPackage> {
148 fn combine(self, other: Self) -> Self {
150 match (self, other) {
151 (Some(mut a), Some(b)) => {
152 for (key, value) in b {
154 a.entry(key).or_insert(value);
155 }
156 Some(a)
157 }
158 (a, b) => a.or(b),
159 }
160 }
161}
162
163impl Combine for Option<ConfigSettings> {
164 fn combine(self, other: Self) -> Self {
167 match (self, other) {
168 (Some(a), Some(b)) => Some(a.merge(b)),
169 (a, b) => a.or(b),
170 }
171 }
172}
173
174impl Combine for Option<PackageConfigSettings> {
175 fn combine(self, other: Self) -> Self {
178 match (self, other) {
179 (Some(a), Some(b)) => Some(a.merge(b)),
180 (a, b) => a.or(b),
181 }
182 }
183}
184
185impl Combine for Option<NoSources> {
186 fn combine(self, other: Self) -> Self {
188 match (self, other) {
189 (Some(a), Some(b)) => Some(a.combine(b)),
190 (a, b) => a.or(b),
191 }
192 }
193}
194
195impl Combine for Option<Upgrade> {
196 fn combine(self, other: Self) -> Self {
197 match (self, other) {
198 (Some(a), Some(b)) => Some(a.combine(b)),
199 (a, b) => a.or(b),
200 }
201 }
202}
203
204impl Combine for Option<Reinstall> {
205 fn combine(self, other: Self) -> Self {
206 match (self, other) {
207 (Some(a), Some(b)) => Some(a.combine(b)),
208 (a, b) => a.or(b),
209 }
210 }
211}
212
213impl Combine for Option<BuildIsolation> {
214 fn combine(self, other: Self) -> Self {
215 match (self, other) {
216 (Some(a), Some(b)) => Some(a.combine(b)),
217 (a, b) => a.or(b),
218 }
219 }
220}
221
222impl Combine for serde::de::IgnoredAny {
223 fn combine(self, _other: Self) -> Self {
224 self
225 }
226}
227
228impl Combine for Option<serde::de::IgnoredAny> {
229 fn combine(self, _other: Self) -> Self {
230 self
231 }
232}
233
234impl Combine for ExcludeNewer {
235 fn combine(mut self, other: Self) -> Self {
236 self.global = self.global.combine(other.global);
237
238 if !other.package.is_empty() {
239 if self.package.is_empty() {
240 self.package = other.package;
241 } else {
242 for (pkg, setting) in &other.package {
244 self.package
245 .entry(pkg.clone())
246 .or_insert_with(|| setting.clone());
247 }
248 }
249 }
250
251 self
252 }
253}
254
255impl Combine for ExtraBuildDependencies {
256 fn combine(mut self, other: Self) -> Self {
257 for (key, value) in other {
258 match self.entry(key) {
259 std::collections::btree_map::Entry::Occupied(mut entry) => {
260 let existing = entry.get_mut();
262 existing.extend(value);
263 }
264 std::collections::btree_map::Entry::Vacant(entry) => {
265 entry.insert(value);
266 }
267 }
268 }
269 self
270 }
271}
272
273impl Combine for Option<ExtraBuildDependencies> {
274 fn combine(self, other: Self) -> Self {
275 match (self, other) {
276 (Some(a), Some(b)) => Some(a.combine(b)),
277 (a, b) => a.or(b),
278 }
279 }
280}
281
282impl Combine for ExtraBuildVariables {
283 fn combine(mut self, other: Self) -> Self {
284 for (key, value) in other {
285 match self.entry(key) {
286 std::collections::btree_map::Entry::Occupied(mut entry) => {
287 let existing = entry.get_mut();
289 for (k, v) in value {
290 existing.entry(k).or_insert(v);
291 }
292 }
293 std::collections::btree_map::Entry::Vacant(entry) => {
294 entry.insert(value);
295 }
296 }
297 }
298 self
299 }
300}
301
302impl Combine for Option<ExtraBuildVariables> {
303 fn combine(self, other: Self) -> Self {
304 match (self, other) {
305 (Some(a), Some(b)) => Some(a.combine(b)),
306 (a, b) => a.or(b),
307 }
308 }
309}