Add a derive macro for Combine (#4325)

## Summary

Saves us some boilerplate when adding settings in the future.
This commit is contained in:
Charlie Marsh 2024-06-13 21:53:27 -07:00 committed by GitHub
parent 83067c1802
commit 74c05683bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 71 additions and 123 deletions

9
Cargo.lock generated
View file

@ -4793,6 +4793,14 @@ dependencies = [
"walkdir",
]
[[package]]
name = "uv-macros"
version = "0.0.1"
dependencies = [
"quote",
"syn 2.0.66",
]
[[package]]
name = "uv-normalize"
version = "0.0.1"
@ -4903,6 +4911,7 @@ dependencies = [
"tracing",
"uv-configuration",
"uv-fs",
"uv-macros",
"uv-normalize",
"uv-resolver",
"uv-toolchain",

View file

@ -38,6 +38,7 @@ uv-extract = { path = "crates/uv-extract" }
uv-fs = { path = "crates/uv-fs" }
uv-git = { path = "crates/uv-git" }
uv-installer = { path = "crates/uv-installer" }
uv-macros = { path = "crates/uv-macros" }
uv-normalize = { path = "crates/uv-normalize" }
uv-requirements = { path = "crates/uv-requirements" }
uv-resolver = { path = "crates/uv-resolver" }
@ -103,6 +104,7 @@ platform-info = { version = "2.0.2" }
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "a68cbd1a26e43986a31563e1d127e83bafca3a0c" }
pyo3 = { version = "0.21.0" }
pyo3-log = { version = "0.10.0" }
quote = { version = "1.0.36" }
rayon = { version = "1.8.0" }
reflink-copy = { version = "0.1.15" }
regex = { version = "1.10.2" }
@ -119,6 +121,7 @@ seahash = { version = "4.1.0" }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = { version = "1.0.114" }
sha2 = { version = "0.10.8" }
syn = { version = "2.0.66" }
sys-info = { version = "0.9.1" }
target-lexicon = {version = "0.12.14" }
tempfile = { version = "3.9.0" }
@ -139,10 +142,10 @@ unicode-width = { version = "0.1.11" }
unscanny = { version = "0.1.0" }
url = { version = "2.5.0" }
urlencoding = { version = "2.1.3" }
wiremock = { version = "0.6.0" }
walkdir = { version = "2.5.0" }
which = { version = "6.0.0" }
winapi = { version = "0.3.9", features = ["fileapi", "handleapi", "ioapiset", "winbase", "winioctl", "winnt"] }
wiremock = { version = "0.6.0" }
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
[workspace.metadata.cargo-shear]

View file

@ -0,0 +1,11 @@
[package]
name = "uv-macros"
version = "0.0.1"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
quote = { workspace = true }
syn = { workspace = true }

View file

@ -0,0 +1,40 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(CombineOptions)]
pub fn derive_combine(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
impl_combine(&input)
}
fn impl_combine(ast: &DeriveInput) -> TokenStream {
let name = &ast.ident;
let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
..
}) = ast.data
{
&fields.named
} else {
unimplemented!();
};
let combines = fields.iter().map(|f| {
let name = &f.ident;
quote! {
#name: self.#name.combine(other.#name)
}
});
let gen = quote! {
impl crate::Combine for #name {
fn combine(self, other: #name) -> #name {
#name {
#(#combines),*
}
}
}
};
gen.into()
}

View file

@ -22,6 +22,7 @@ uv-fs = { workspace = true }
uv-normalize = { workspace = true, features = ["schemars"] }
uv-resolver = { workspace = true, features = ["schemars"] }
uv-toolchain = { workspace = true, features = ["schemars"] }
uv-macros = { workspace = true }
dirs-sys = { workspace = true }
fs-err = { workspace = true }

View file

@ -7,7 +7,7 @@ use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, Targe
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::PythonVersion;
use crate::{FilesystemOptions, GlobalOptions, Options, PipOptions, ResolverInstallerOptions};
use crate::{FilesystemOptions, PipOptions};
pub trait Combine {
/// Combine two values, preferring the values in `self`.
@ -37,58 +37,6 @@ impl Combine for Option<FilesystemOptions> {
}
}
impl Combine for Options {
fn combine(self, other: Options) -> Options {
Options {
globals: self.globals.combine(other.globals),
top_level: self.top_level.combine(other.top_level),
pip: self.pip.combine(other.pip),
override_dependencies: self
.override_dependencies
.combine(other.override_dependencies),
}
}
}
impl Combine for GlobalOptions {
fn combine(self, other: GlobalOptions) -> GlobalOptions {
GlobalOptions {
native_tls: self.native_tls.combine(other.native_tls),
offline: self.offline.combine(other.offline),
no_cache: self.no_cache.combine(other.no_cache),
cache_dir: self.cache_dir.combine(other.cache_dir),
preview: self.preview.combine(other.preview),
}
}
}
impl Combine for ResolverInstallerOptions {
fn combine(self, other: ResolverInstallerOptions) -> ResolverInstallerOptions {
ResolverInstallerOptions {
index_url: self.index_url.combine(other.index_url),
extra_index_url: self.extra_index_url.combine(other.extra_index_url),
no_index: self.no_index.combine(other.no_index),
find_links: self.find_links.combine(other.find_links),
index_strategy: self.index_strategy.combine(other.index_strategy),
keyring_provider: self.keyring_provider.combine(other.keyring_provider),
resolution: self.resolution.combine(other.resolution),
prerelease: self.prerelease.combine(other.prerelease),
config_settings: self.config_settings.combine(other.config_settings),
exclude_newer: self.exclude_newer.combine(other.exclude_newer),
link_mode: self.link_mode.combine(other.link_mode),
compile_bytecode: self.compile_bytecode.combine(other.compile_bytecode),
upgrade: self.upgrade.combine(other.upgrade),
upgrade_package: self.upgrade_package.combine(other.upgrade_package),
reinstall: self.reinstall.combine(other.reinstall),
reinstall_package: self.reinstall_package.combine(other.reinstall_package),
no_build: self.no_build.combine(other.no_build),
no_build_package: self.no_build_package.combine(other.no_build_package),
no_binary: self.no_binary.combine(other.no_binary),
no_binary_package: self.no_binary_package.combine(other.no_binary_package),
}
}
}
impl Combine for Option<PipOptions> {
fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> {
match (self, other) {
@ -98,71 +46,6 @@ impl Combine for Option<PipOptions> {
}
}
impl Combine for PipOptions {
fn combine(self, other: PipOptions) -> PipOptions {
PipOptions {
python: self.python.combine(other.python),
system: self.system.combine(other.system),
break_system_packages: self
.break_system_packages
.combine(other.break_system_packages),
target: self.target.combine(other.target),
prefix: self.prefix.combine(other.prefix),
index_url: self.index_url.combine(other.index_url),
extra_index_url: self.extra_index_url.combine(other.extra_index_url),
no_index: self.no_index.combine(other.no_index),
find_links: self.find_links.combine(other.find_links),
index_strategy: self.index_strategy.combine(other.index_strategy),
keyring_provider: self.keyring_provider.combine(other.keyring_provider),
no_build: self.no_build.combine(other.no_build),
no_binary: self.no_binary.combine(other.no_binary),
only_binary: self.only_binary.combine(other.only_binary),
no_build_isolation: self.no_build_isolation.combine(other.no_build_isolation),
strict: self.strict.combine(other.strict),
extra: self.extra.combine(other.extra),
all_extras: self.all_extras.combine(other.all_extras),
no_deps: self.no_deps.combine(other.no_deps),
resolution: self.resolution.combine(other.resolution),
prerelease: self.prerelease.combine(other.prerelease),
output_file: self.output_file.combine(other.output_file),
no_strip_extras: self.no_strip_extras.combine(other.no_strip_extras),
no_annotate: self.no_annotate.combine(other.no_annotate),
no_header: self.no_header.combine(other.no_header),
custom_compile_command: self
.custom_compile_command
.combine(other.custom_compile_command),
generate_hashes: self.generate_hashes.combine(other.generate_hashes),
legacy_setup_py: self.legacy_setup_py.combine(other.legacy_setup_py),
config_settings: self.config_settings.combine(other.config_settings),
python_version: self.python_version.combine(other.python_version),
python_platform: self.python_platform.combine(other.python_platform),
exclude_newer: self.exclude_newer.combine(other.exclude_newer),
no_emit_package: self.no_emit_package.combine(other.no_emit_package),
emit_index_url: self.emit_index_url.combine(other.emit_index_url),
emit_find_links: self.emit_find_links.combine(other.emit_find_links),
emit_marker_expression: self
.emit_marker_expression
.combine(other.emit_marker_expression),
emit_index_annotation: self
.emit_index_annotation
.combine(other.emit_index_annotation),
annotation_style: self.annotation_style.combine(other.annotation_style),
link_mode: self.link_mode.combine(other.link_mode),
compile_bytecode: self.compile_bytecode.combine(other.compile_bytecode),
require_hashes: self.require_hashes.combine(other.require_hashes),
upgrade: self.upgrade.combine(other.upgrade),
upgrade_package: self.upgrade_package.combine(other.upgrade_package),
reinstall: self.reinstall.combine(other.reinstall),
reinstall_package: self.reinstall_package.combine(other.reinstall_package),
concurrent_downloads: self
.concurrent_downloads
.combine(other.concurrent_downloads),
concurrent_builds: self.concurrent_builds.combine(other.concurrent_builds),
concurrent_installs: self.concurrent_installs.combine(other.concurrent_installs),
}
}
}
macro_rules! impl_combine_or {
($name:ident) => {
impl Combine for Option<$name> {

View file

@ -8,6 +8,7 @@ use pypi_types::VerbatimParsedUrl;
use uv_configuration::{
ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple,
};
use uv_macros::CombineOptions;
use uv_normalize::{ExtraName, PackageName};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::PythonVersion;
@ -28,7 +29,7 @@ pub(crate) struct Tools {
/// A `[tool.uv]` section.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Options {
@ -49,7 +50,7 @@ pub struct Options {
/// Global settings, relevant to all invocations.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct GlobalOptions {
@ -111,7 +112,7 @@ pub struct ResolverOptions {
/// Shared settings, relevant to all operations that must resolve and install dependencies. The
/// union of [`InstallerOptions`] and [`ResolverOptions`].
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ResolverInstallerOptions {
@ -139,7 +140,7 @@ pub struct ResolverInstallerOptions {
/// A `[tool.uv.pip]` section.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PipOptions {