1use std::path::PathBuf;
2use std::{collections::BTreeMap, num::NonZeroUsize};
3
4use url::Url;
5
6use uv_configuration::{
7 BuildIsolation, ExportFormat, IndexStrategy, KeyringProviderType, Reinstall, RequiredVersion,
8 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!(PythonDownloads);
104impl_combine_or!(PythonPreference);
105impl_combine_or!(PythonVersion);
106impl_combine_or!(RequiredVersion);
107impl_combine_or!(ResolutionMode);
108impl_combine_or!(SchemaConflicts);
109impl_combine_or!(String);
110impl_combine_or!(SupportedEnvironments);
111impl_combine_or!(TargetTriple);
112impl_combine_or!(TorchMode);
113impl_combine_or!(TrustedPublishing);
114impl_combine_or!(Url);
115impl_combine_or!(bool);
116
117impl<T> Combine for Option<Vec<T>> {
118 fn combine(self, other: Self) -> Self {
121 match (self, other) {
122 (Some(mut a), Some(b)) => {
123 a.extend(b);
124 Some(a)
125 }
126 (a, b) => a.or(b),
127 }
128 }
129}
130
131impl<K: Ord, T> Combine for Option<BTreeMap<K, Vec<T>>> {
132 fn combine(self, other: Self) -> Self {
134 match (self, other) {
135 (Some(mut a), Some(b)) => {
136 for (key, value) in b {
137 a.entry(key).or_default().extend(value);
138 }
139 Some(a)
140 }
141 (a, b) => a.or(b),
142 }
143 }
144}
145
146impl Combine for Option<ExcludeNewerPackage> {
147 fn combine(self, other: Self) -> Self {
149 match (self, other) {
150 (Some(mut a), Some(b)) => {
151 for (key, value) in b {
153 a.entry(key).or_insert(value);
154 }
155 Some(a)
156 }
157 (a, b) => a.or(b),
158 }
159 }
160}
161
162impl Combine for Option<ConfigSettings> {
163 fn combine(self, other: Self) -> Self {
166 match (self, other) {
167 (Some(a), Some(b)) => Some(a.merge(b)),
168 (a, b) => a.or(b),
169 }
170 }
171}
172
173impl Combine for Option<PackageConfigSettings> {
174 fn combine(self, other: Self) -> Self {
177 match (self, other) {
178 (Some(a), Some(b)) => Some(a.merge(b)),
179 (a, b) => a.or(b),
180 }
181 }
182}
183
184impl Combine for Option<Upgrade> {
185 fn combine(self, other: Self) -> Self {
186 match (self, other) {
187 (Some(a), Some(b)) => Some(a.combine(b)),
188 (a, b) => a.or(b),
189 }
190 }
191}
192
193impl Combine for Option<Reinstall> {
194 fn combine(self, other: Self) -> Self {
195 match (self, other) {
196 (Some(a), Some(b)) => Some(a.combine(b)),
197 (a, b) => a.or(b),
198 }
199 }
200}
201
202impl Combine for Option<BuildIsolation> {
203 fn combine(self, other: Self) -> Self {
204 match (self, other) {
205 (Some(a), Some(b)) => Some(a.combine(b)),
206 (a, b) => a.or(b),
207 }
208 }
209}
210
211impl Combine for serde::de::IgnoredAny {
212 fn combine(self, _other: Self) -> Self {
213 self
214 }
215}
216
217impl Combine for Option<serde::de::IgnoredAny> {
218 fn combine(self, _other: Self) -> Self {
219 self
220 }
221}
222
223impl Combine for ExcludeNewer {
224 fn combine(mut self, other: Self) -> Self {
225 self.global = self.global.combine(other.global);
226
227 if !other.package.is_empty() {
228 if self.package.is_empty() {
229 self.package = other.package;
230 } else {
231 for (pkg, timestamp) in &other.package {
233 self.package.entry(pkg.clone()).or_insert(timestamp.clone());
234 }
235 }
236 }
237
238 self
239 }
240}
241
242impl Combine for ExtraBuildDependencies {
243 fn combine(mut self, other: Self) -> Self {
244 for (key, value) in other {
245 match self.entry(key) {
246 std::collections::btree_map::Entry::Occupied(mut entry) => {
247 let existing = entry.get_mut();
249 existing.extend(value);
250 }
251 std::collections::btree_map::Entry::Vacant(entry) => {
252 entry.insert(value);
253 }
254 }
255 }
256 self
257 }
258}
259
260impl Combine for Option<ExtraBuildDependencies> {
261 fn combine(self, other: Self) -> Self {
262 match (self, other) {
263 (Some(a), Some(b)) => Some(a.combine(b)),
264 (a, b) => a.or(b),
265 }
266 }
267}
268
269impl Combine for ExtraBuildVariables {
270 fn combine(mut self, other: Self) -> Self {
271 for (key, value) in other {
272 match self.entry(key) {
273 std::collections::btree_map::Entry::Occupied(mut entry) => {
274 let existing = entry.get_mut();
276 for (k, v) in value {
277 existing.entry(k).or_insert(v);
278 }
279 }
280 std::collections::btree_map::Entry::Vacant(entry) => {
281 entry.insert(value);
282 }
283 }
284 }
285 self
286 }
287}
288
289impl Combine for Option<ExtraBuildVariables> {
290 fn combine(self, other: Self) -> Self {
291 match (self, other) {
292 (Some(a), Some(b)) => Some(a.combine(b)),
293 (a, b) => a.or(b),
294 }
295 }
296}