mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35: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",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uv-macros"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-normalize"
|
name = "uv-normalize"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -4903,6 +4911,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"uv-configuration",
|
"uv-configuration",
|
||||||
"uv-fs",
|
"uv-fs",
|
||||||
|
"uv-macros",
|
||||||
"uv-normalize",
|
"uv-normalize",
|
||||||
"uv-resolver",
|
"uv-resolver",
|
||||||
"uv-toolchain",
|
"uv-toolchain",
|
||||||
|
|
|
@ -38,6 +38,7 @@ uv-extract = { path = "crates/uv-extract" }
|
||||||
uv-fs = { path = "crates/uv-fs" }
|
uv-fs = { path = "crates/uv-fs" }
|
||||||
uv-git = { path = "crates/uv-git" }
|
uv-git = { path = "crates/uv-git" }
|
||||||
uv-installer = { path = "crates/uv-installer" }
|
uv-installer = { path = "crates/uv-installer" }
|
||||||
|
uv-macros = { path = "crates/uv-macros" }
|
||||||
uv-normalize = { path = "crates/uv-normalize" }
|
uv-normalize = { path = "crates/uv-normalize" }
|
||||||
uv-requirements = { path = "crates/uv-requirements" }
|
uv-requirements = { path = "crates/uv-requirements" }
|
||||||
uv-resolver = { path = "crates/uv-resolver" }
|
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" }
|
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "a68cbd1a26e43986a31563e1d127e83bafca3a0c" }
|
||||||
pyo3 = { version = "0.21.0" }
|
pyo3 = { version = "0.21.0" }
|
||||||
pyo3-log = { version = "0.10.0" }
|
pyo3-log = { version = "0.10.0" }
|
||||||
|
quote = { version = "1.0.36" }
|
||||||
rayon = { version = "1.8.0" }
|
rayon = { version = "1.8.0" }
|
||||||
reflink-copy = { version = "0.1.15" }
|
reflink-copy = { version = "0.1.15" }
|
||||||
regex = { version = "1.10.2" }
|
regex = { version = "1.10.2" }
|
||||||
|
@ -119,6 +121,7 @@ seahash = { version = "4.1.0" }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.114" }
|
serde_json = { version = "1.0.114" }
|
||||||
sha2 = { version = "0.10.8" }
|
sha2 = { version = "0.10.8" }
|
||||||
|
syn = { version = "2.0.66" }
|
||||||
sys-info = { version = "0.9.1" }
|
sys-info = { version = "0.9.1" }
|
||||||
target-lexicon = {version = "0.12.14" }
|
target-lexicon = {version = "0.12.14" }
|
||||||
tempfile = { version = "3.9.0" }
|
tempfile = { version = "3.9.0" }
|
||||||
|
@ -139,10 +142,10 @@ unicode-width = { version = "0.1.11" }
|
||||||
unscanny = { version = "0.1.0" }
|
unscanny = { version = "0.1.0" }
|
||||||
url = { version = "2.5.0" }
|
url = { version = "2.5.0" }
|
||||||
urlencoding = { version = "2.1.3" }
|
urlencoding = { version = "2.1.3" }
|
||||||
wiremock = { version = "0.6.0" }
|
|
||||||
walkdir = { version = "2.5.0" }
|
walkdir = { version = "2.5.0" }
|
||||||
which = { version = "6.0.0" }
|
which = { version = "6.0.0" }
|
||||||
winapi = { version = "0.3.9", features = ["fileapi", "handleapi", "ioapiset", "winbase", "winioctl", "winnt"] }
|
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"] }
|
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
|
||||||
|
|
||||||
[workspace.metadata.cargo-shear]
|
[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-normalize = { workspace = true, features = ["schemars"] }
|
||||||
uv-resolver = { workspace = true, features = ["schemars"] }
|
uv-resolver = { workspace = true, features = ["schemars"] }
|
||||||
uv-toolchain = { workspace = true, features = ["schemars"] }
|
uv-toolchain = { workspace = true, features = ["schemars"] }
|
||||||
|
uv-macros = { workspace = true }
|
||||||
|
|
||||||
dirs-sys = { workspace = true }
|
dirs-sys = { workspace = true }
|
||||||
fs-err = { 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_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
||||||
use uv_toolchain::PythonVersion;
|
use uv_toolchain::PythonVersion;
|
||||||
|
|
||||||
use crate::{FilesystemOptions, GlobalOptions, Options, PipOptions, ResolverInstallerOptions};
|
use crate::{FilesystemOptions, PipOptions};
|
||||||
|
|
||||||
pub trait Combine {
|
pub trait Combine {
|
||||||
/// Combine two values, preferring the values in `self`.
|
/// 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> {
|
impl Combine for Option<PipOptions> {
|
||||||
fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> {
|
fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> {
|
||||||
match (self, other) {
|
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 {
|
macro_rules! impl_combine_or {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
impl Combine for Option<$name> {
|
impl Combine for Option<$name> {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use pypi_types::VerbatimParsedUrl;
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple,
|
ConfigSettings, IndexStrategy, KeyringProviderType, PackageNameSpecifier, TargetTriple,
|
||||||
};
|
};
|
||||||
|
use uv_macros::CombineOptions;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
||||||
use uv_toolchain::PythonVersion;
|
use uv_toolchain::PythonVersion;
|
||||||
|
@ -28,7 +29,7 @@ pub(crate) struct Tools {
|
||||||
|
|
||||||
/// A `[tool.uv]` section.
|
/// A `[tool.uv]` section.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
@ -49,7 +50,7 @@ pub struct Options {
|
||||||
|
|
||||||
/// Global settings, relevant to all invocations.
|
/// Global settings, relevant to all invocations.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct GlobalOptions {
|
pub struct GlobalOptions {
|
||||||
|
@ -111,7 +112,7 @@ pub struct ResolverOptions {
|
||||||
/// Shared settings, relevant to all operations that must resolve and install dependencies. The
|
/// Shared settings, relevant to all operations that must resolve and install dependencies. The
|
||||||
/// union of [`InstallerOptions`] and [`ResolverOptions`].
|
/// union of [`InstallerOptions`] and [`ResolverOptions`].
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct ResolverInstallerOptions {
|
pub struct ResolverInstallerOptions {
|
||||||
|
@ -139,7 +140,7 @@ pub struct ResolverInstallerOptions {
|
||||||
|
|
||||||
/// A `[tool.uv.pip]` section.
|
/// A `[tool.uv.pip]` section.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize, CombineOptions)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct PipOptions {
|
pub struct PipOptions {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue