From cf0aad78c48f8460fa2984dbdb0a443124066db6 Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Sun, 30 Aug 2026 10:15:16 +0800 Subject: [PATCH 1/3] catalyst: refine substrate as subfeature --- complex-example/Cargo.toml | 2 +- complex-example/catalyst-src/Cargo.toml | 2 +- complex-example/catalyst/Cargo.toml | 2 +- complex-example/substrate/Cargo.toml | 2 +- derive/Cargo.toml | 3 ++- derive/src/lib.rs | 6 +++--- lib/Cargo.toml | 6 +++++- lib/src/lib.rs | 2 +- lib/src/traits.rs | 2 +- 9 files changed, 16 insertions(+), 11 deletions(-) diff --git a/complex-example/Cargo.toml b/complex-example/Cargo.toml index 9744b47..ac701da 100644 --- a/complex-example/Cargo.toml +++ b/complex-example/Cargo.toml @@ -6,7 +6,7 @@ members = [ "catalyst-src", ] [workspace.dependencies] -struct-patch = { path = "../lib", features = ["catalyst"] } +struct-patch = { path = "../lib" } [workspace.package] authors = ["Antonio Yang "] diff --git a/complex-example/catalyst-src/Cargo.toml b/complex-example/catalyst-src/Cargo.toml index 1b25782..8657d4a 100644 --- a/complex-example/catalyst-src/Cargo.toml +++ b/complex-example/catalyst-src/Cargo.toml @@ -12,7 +12,7 @@ description.workspace = true rust-version.workspace = true [dependencies] -struct-patch.workspace = true +struct-patch = { workspace = true, features = ["catalyst"] } substrate = { path = "../substrate" } serde = { version = "1", features = ["derive"] } toml = "1.1.2" diff --git a/complex-example/catalyst/Cargo.toml b/complex-example/catalyst/Cargo.toml index 705f466..997e369 100644 --- a/complex-example/catalyst/Cargo.toml +++ b/complex-example/catalyst/Cargo.toml @@ -12,7 +12,7 @@ description.workspace = true rust-version.workspace = true [dependencies] -struct-patch.workspace = true +struct-patch = { workspace = true, features = ["catalyst"] } substrate = { path = "../substrate" } serde = { version = "1", features = ["derive"] } toml = "1.1.2" diff --git a/complex-example/substrate/Cargo.toml b/complex-example/substrate/Cargo.toml index 612aa78..4de5a3d 100644 --- a/complex-example/substrate/Cargo.toml +++ b/complex-example/substrate/Cargo.toml @@ -12,7 +12,7 @@ description.workspace = true rust-version.workspace = true [dependencies] -struct-patch.workspace = true +struct-patch = { workspace = true, features = ["substrate"] } serde = { version = "1", features = ["derive"] } [dev-dependencies] diff --git a/derive/Cargo.toml b/derive/Cargo.toml index d18e5cb..95b806f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -26,7 +26,8 @@ status = [] op = [] merge = [] nesting = [] -catalyst = [ "syn-serde", "dep:cargo_metadata", "syn/full" ] +substrate = [ "syn-serde", "syn/full" ] +catalyst = [ "substrate", "dep:cargo_metadata" ] unsafe = [] box = [] diff --git a/derive/src/lib.rs b/derive/src/lib.rs index e41ce64..f66f4ae 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -3,14 +3,14 @@ extern crate proc_macro; mod catalyst; mod filler; mod patch; -#[cfg(feature = "catalyst")] +#[cfg(feature = "substrate")] mod substrate; #[cfg(feature = "catalyst")] use catalyst::Catalyst; use filler::Filler; use patch::Patch; -#[cfg(feature = "catalyst")] +#[cfg(feature = "substrate")] use substrate::Substrate; use syn::meta::ParseNestedMeta; @@ -42,7 +42,7 @@ pub fn derive_filler(item: proc_macro::TokenStream) -> proc_macro::TokenStream { .into() } -#[cfg(feature = "catalyst")] +#[cfg(feature = "substrate")] #[proc_macro_derive(Substrate, attributes(substrate))] pub fn derive_substrate(item: proc_macro::TokenStream) -> proc_macro::TokenStream { Substrate::from_ast(syn::parse_macro_input!(item as syn::DeriveInput)) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index e6a9a82..d9a0618 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -43,8 +43,12 @@ nesting = [ ] none_as_default = ["option"] keep_none = ["option"] +substrate = [ + "struct-patch-derive/substrate" +] catalyst = [ - "struct-patch-derive/catalyst" + "struct-patch-derive/catalyst", + "struct-patch-derive/substrate" ] unsafe = [ "struct-patch-derive/unsafe" diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 8735ea4..ad9d3f3 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -85,7 +85,7 @@ pub use struct_patch_derive::Catalyst; pub use struct_patch_derive::Filler; #[doc(hidden)] pub use struct_patch_derive::Patch; -#[cfg(feature = "catalyst")] +#[cfg(feature = "substrate")] #[doc(hidden)] pub use struct_patch_derive::Substrate; pub mod r#box; diff --git a/lib/src/traits.rs b/lib/src/traits.rs index fa70aad..9e63c3c 100644 --- a/lib/src/traits.rs +++ b/lib/src/traits.rs @@ -214,7 +214,7 @@ pub trait Merge { fn merge(self, other: Self) -> Self; } -#[cfg(feature = "catalyst")] +#[cfg(feature = "substrate")] /// A substrate struct that can expose the fields information thereof pub trait Substrate { fn expose_content() -> &'static str; From d49f41711d075bf23469f755d522d659494a75bd Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Sun, 30 Aug 2026 10:17:27 +0800 Subject: [PATCH 2/3] bump 0.14.1 --- Cargo.toml | 2 +- complex-example/Cargo.toml | 2 +- lib/Cargo.toml | 2 +- no-std-examples/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7e72af..f013e96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ exclude = [ [workspace.package] authors = ["Antonio Yang "] -version = "0.14.0" +version = "0.14.1" edition = "2021" categories = ["development-tools"] keywords = ["struct", "patch", "macro", "derive", "overlay"] diff --git a/complex-example/Cargo.toml b/complex-example/Cargo.toml index ac701da..52cfb06 100644 --- a/complex-example/Cargo.toml +++ b/complex-example/Cargo.toml @@ -10,7 +10,7 @@ struct-patch = { path = "../lib" } [workspace.package] authors = ["Antonio Yang "] -version = "0.14.0" +version = "0.14.1" edition = "2021" categories = ["development-tools"] keywords = ["struct", "patch", "macro", "derive", "overlay"] diff --git a/lib/Cargo.toml b/lib/Cargo.toml index d9a0618..b9e9822 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -12,7 +12,7 @@ readme.workspace = true rust-version.workspace = true [dependencies] -struct-patch-derive = { version = "=0.14.0", path = "../derive" } +struct-patch-derive = { version = "=0.14.1", path = "../derive" } [dev-dependencies] serde_json = "1.0" diff --git a/no-std-examples/Cargo.toml b/no-std-examples/Cargo.toml index 80a1633..ddaaf9f 100644 --- a/no-std-examples/Cargo.toml +++ b/no-std-examples/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "no-std-examples" authors = ["Antonio Yang "] -version = "0.14.0" +version = "0.14.1" edition = "2021" license = "MIT" From 70b6ba7eeaffef7f9c58ba34d5ed02a3388ce25e Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Sun, 30 Aug 2026 10:19:56 +0800 Subject: [PATCH 3/3] readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 34b7818..29a65b9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This crate provides the `Patch`, `Filler`, `Substrate`, `Catalyst` and `Complex` - If any field in a `Patch` is `Some`, it overwrites the corresponding field when applied. Use `apply_with_log` to receive a callback for each field that is actually changed. - If any field in the instance is empty (`None` or an empty collection), `Filler` will try to fill it. It supports `Option`, `Vec`, `VecDeque`, `LinkedList`, `HashMap`, `BTreeMap`, `HashSet`, `BTreeSet`, `BinaryHeap` fields, as well as custom types via `#[filler(extendable)]` and any type via `#[filler(empty_value = ...)]`. -- With the `catalyst` feature, `Substrate`, `Catalyst` and `Complex` traits with accompanying derive macros help you extend a struct with extra fields from another crate. +- With the `substrate` feature, the `Substrate` derive macro exposes field information so that other crates can access it. With the `catalyst` feature (which implies `substrate`), `Catalyst` and `Complex` derive macros help you extend a struct with extra fields from another crate. This crate supports `no_std` — check the [no-std-examples](./no-std-examples). @@ -264,7 +264,8 @@ This crate includes the following optional features: - `none_as_default` *(optional)*: `T` needs to implement `Default`. When patching on `None`, it patches on a default instance. Mutually exclusive with `keep_none`. - `keep_none` *(optional)*: when patching on `None`, it stays `None`. Mutually exclusive with `none_as_default`. - `nesting` *(optional)*: allows a field to use `Patch` derive with the `#[patch(nesting)]` attribute. -- `catalyst` *(optional)*: enables the `Substrate`, `Catalyst`, and `Complex` derive macros for extending a struct with fields from another crate. +- `substrate` *(optional)*: enables the `Substrate` derive macro for exposing a struct's field layout so downstream crates can access it via `expose()` or source parsing. +- `catalyst` *(optional)*: enables the `Catalyst` and `Complex` derive macros for extending a struct with fields from another crate. Implies `substrate`. - `unsafe` *(optional)*: uses `ManuallyDrop` + `ptr::read` / `MaybeUninit` + `ptr::write` in the generated `bind`, `decouple`, `__substrate_new`, and `__substrate_unpack` to avoid memory moves. Only meaningful with the `catalyst` feature. [crates-badge]: https://img.shields.io/crates/v/struct-patch.svg