From 31629abaf6808bad8bc591c36b82f48a1c723a48 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 24 Aug 2026 16:57:20 +0300 Subject: [PATCH] feat: Move `rename_all` behind a new `display_rename_all` feature `display` pulls `convert_case`, hence `unicode-segmentation`, which some consumers cannot vendor. It is only needed for the optional `#[display(rename_all = "...")]` attribute. --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 ++ impl/Cargo.toml | 4 +++- impl/doc/display.md | 3 +++ impl/src/fmt/display.rs | 22 ++++++++++++++++++---- impl/src/utils.rs | 1 + 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 833502c2..548678a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## master +### Breaking changes + +- `#[display(rename_all = "...")]` now requires the `display_rename_all` feature, + which is included in `full`. This keeps the `display` feature free of the + `convert_case` dependency. + ### Added - Add `Hash` derive similar to `std`'s one, but considering generics correctly, and supporting custom hash functions per field or skipping fields. diff --git a/Cargo.toml b/Cargo.toml index 5e9c4cc1..b959262d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ debug = ["derive_more-impl/debug"] deref = ["derive_more-impl/deref"] deref_mut = ["derive_more-impl/deref_mut"] display = ["derive_more-impl/display"] +display_rename_all = ["display", "derive_more-impl/display_rename_all"] eq = ["derive_more-impl/eq"] error = ["derive_more-impl/error"] from = ["derive_more-impl/from"] @@ -90,6 +91,7 @@ full = [ "deref", "deref_mut", "display", + "display_rename_all", "eq", "error", "from", diff --git a/impl/Cargo.toml b/impl/Cargo.toml index cfd82220..f20c4e49 100644 --- a/impl/Cargo.toml +++ b/impl/Cargo.toml @@ -56,7 +56,8 @@ constructor = [] debug = ["syn/extra-traits", "dep:unicode-ident"] deref = [] deref_mut = [] -display = ["syn/extra-traits", "dep:unicode-ident", "dep:convert_case"] +display = ["syn/extra-traits", "dep:unicode-ident"] +display_rename_all = ["display", "dep:convert_case"] eq = ["syn/extra-traits", "syn/visit"] error = ["syn/extra-traits"] from = ["syn/extra-traits"] @@ -85,6 +86,7 @@ full = [ "deref", "deref_mut", "display", + "display_rename_all", "eq", "error", "from", diff --git a/impl/doc/display.md b/impl/doc/display.md index fbc9b23c..52e651e6 100644 --- a/impl/doc/display.md +++ b/impl/doc/display.md @@ -233,6 +233,9 @@ assert_eq!(Enum::C.to_string(), "c"); When no format is specified, deriving `Display` uses the variant name verbatim as its format. To control this the `#[display(rename_all = "...")]` attribute can be placed on structs, enums and variants. +This attribute requires the `display_rename_all` feature, which is part of `full`. It is separate +from `display` because the case conversion pulls in the `convert_case` dependency. + The available casings are: - `lowercase` - `UPPERCASE` diff --git a/impl/src/fmt/display.rs b/impl/src/fmt/display.rs index b59552d8..a0a71866 100644 --- a/impl/src/fmt/display.rs +++ b/impl/src/fmt/display.rs @@ -103,6 +103,17 @@ struct ContainerAttributes { common: super::ContainerAttributes, } +impl ContainerAttributes { + /// Applies the [`attr::RenameAll`] of these attributes to the provided `name`, if any. + fn rename(&self, name: String) -> String { + #[cfg(feature = "display_rename_all")] + if let Some(rename_all) = &self.rename_all { + return rename_all.convert_case(&name); + } + name + } +} + impl Parse for ContainerAttributes { fn parse(input: ParseStream<'_>) -> syn::Result { mod ident { @@ -130,6 +141,12 @@ impl Parse for ContainerAttributes { ..Default::default() }) } else if ahead.peek(ident::rename_all) { + if !cfg!(feature = "display_rename_all") { + return Err(syn::Error::new( + input.span(), + "`rename_all` requires the `display_rename_all` feature of `derive_more`", + )); + } Ok(Self { rename_all: Some(input.parse()?), ..Self::default() @@ -427,10 +444,7 @@ impl Expansion<'_> { None => { if shared_attr_is_wrapping || !has_shared_attr { body = if self.fields.is_empty() { - let mut ident_str = self.ident.unraw().to_string(); - if let Some(rename_all) = &self.attrs.rename_all { - ident_str = rename_all.convert_case(&ident_str); - } + let ident_str = self.attrs.rename(self.ident.unraw().to_string()); if shared_attr_is_wrapping { quote! { #ident_str } diff --git a/impl/src/utils.rs b/impl/src/utils.rs index 82178bb1..c505043b 100644 --- a/impl/src/utils.rs +++ b/impl/src/utils.rs @@ -2344,6 +2344,7 @@ pub(crate) mod attr { impl RenameAll { /// Converts the provided `name` into the case of this [`RenameAll`]. + #[cfg(any(feature = "display_rename_all", feature = "from_str"))] pub(crate) fn convert_case(&self, name: &str) -> String { use convert_case::Casing as _;