mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Add a derive macro for Combine
(#4325)
## Summary Saves us some boilerplate when adding settings in the future.
This commit is contained in:
parent
83067c1802
commit
74c05683bb
7 changed files with 71 additions and 123 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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]
|
||||
|
|
11
crates/uv-macros/Cargo.toml
Normal file
11
crates/uv-macros/Cargo.toml
Normal 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 }
|
40
crates/uv-macros/src/lib.rs
Normal file
40
crates/uv-macros/src/lib.rs
Normal 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()
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue