Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exclude = [

[workspace.package]
authors = ["Antonio Yang <yanganto@gmail.com>"]
version = "0.14.3"
version = "0.14.4"
edition = "2021"
categories = ["development-tools"]
keywords = ["struct", "patch", "macro", "derive", "overlay"]
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ Two attribute namespaces are provided for the catalyst feature because we need t
- `#[patch(name = "...")]`: change the name of the generated patch struct.
- `#[patch(attribute(...))]`: add attributes to the generated patch struct.
- `#[patch(attribute(derive(...)))]`: add derives to the generated patch struct.
- `#[patch(default_log(fn_path))]`: call `fn_path` with each patched field name on every `apply` call. Has no effect on `apply_with_log`. Function signature: `fn(&str)` without nesting feature, or `fn(&[&str], &str)` with nesting feature.
- `#[patch(default_log(fn_path))]`: call `fn_path` with each patched field name on every `apply` call. Has no effect on `apply_with_log`. Function signature: `fn(&str)` without nesting feature, `fn(&str, &str)` with `simple-nesting` feature, or `fn(&[&str], &str)` with nesting feature.
- `#[filler(attribute(...))]`: add attributes to the generated filler struct.
- `#[filler(default_log(fn_path))]`: call `fn_path` with each filled field name on every `apply` call. Has no effect on `apply_with_log`. Function signature: `fn(&str)` without nesting feature, or `fn(&[&str], &str)` with nesting feature.
- `#[filler(default_log(fn_path))]`: call `fn_path` with each filled field name on every `apply` call. Has no effect on `apply_with_log`. Function signature: `fn(&str)` without nesting feature, `fn(&str, &str)` with `simple-nesting` feature, or `fn(&[&str], &str)` with nesting feature.
- `#[catalyst(bind = ...)]`: specify the base (substrate) structure. Need substrate expose() in build (catalyst feature)
- `#[catalyst(bind = ..., src = "crate_name:/path/to/file")]`: specify the base (substrate) structure. No need substrate expose() and based on source code. Avoide syn protocol change (catalyst feature)
- `#[catalyst(keep_field_attribute)]`: pass all field attributes from a substrate or catalyst through to the complex, unless an override is explicitly specified for that field. (catalyst feature)
Expand Down Expand Up @@ -273,6 +273,7 @@ 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.
- simple-`nesting` *(optional)*: allows a field to use `Patch` derive with the `#[patch(nesting)]` attribute, only one layer nesting, in which the log in `fn(prefix: &str, field: &str)` without vector, such that is good for no-std without alloc.
- `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)*: avoid memory moves. Only meaningful with the `catalyst` feature.
Expand Down
2 changes: 2 additions & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ cargo_metadata = { version = "0.23", optional = true }
status = []
op = []
merge = []
log = []
nesting = []
simple-nesting = ["nesting"]
substrate = [ "syn-serde", "syn/full" ]
catalyst = [ "substrate", "dep:cargo_metadata" ]
unsafe = []
Expand Down
92 changes: 85 additions & 7 deletions derive/src/filler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) struct Filler {
generics: syn::Generics,
attributes: Vec<TokenStream>,
fields: Vec<Field>,
#[cfg(feature = "log")]
default_log_fn: Option<syn::Path>,
}

Expand Down Expand Up @@ -70,6 +71,7 @@ impl Filler {
generics,
attributes,
fields,
#[cfg(feature = "log")]
default_log_fn,
} = self;

Expand Down Expand Up @@ -248,6 +250,7 @@ impl Filler {
let op_impl = quote!();

// Per-field log-call token streams, parallel with each field-name vec.
#[cfg(feature = "log")]
let make_log_calls = |names: &[Option<&Ident>]| -> Vec<TokenStream> {
if let Some(f) = default_log_fn {
#[cfg(feature = "nesting")]
Expand All @@ -267,17 +270,22 @@ impl Filler {
}
names.iter().map(|_| quote! {}).collect()
};
#[cfg(not(feature = "log"))]
let make_log_calls = |names: &[Option<&Ident>]| -> Vec<TokenStream> {
names.iter().map(|_| quote! {}).collect()
};

let native_value_log_calls = make_log_calls(&native_value_field_names);
let extendable_log_calls = make_log_calls(&extendable_field_names);
let option_log_calls = make_log_calls(&option_field_names);

// For the `apply` method: propagate `default_log_fn` into nesting fields
#[cfg(feature = "nesting")]
#[cfg(all(feature = "log", feature = "simple-nesting"))]
let nesting_apply_section: TokenStream = if let Some(ref f) = default_log_fn {
quote! {
#(
self.#nesting_field_names.apply_with_log(filler.#nesting_field_names, |_prefixes: &[&str], field: &str| {
#f(&[], field);
self.#nesting_field_names.apply_with_log(filler.#nesting_field_names, |_prefix: &str, field: &str| {
#f(stringify!(#nesting_field_names), field);
});
)*
}
Expand All @@ -288,10 +296,41 @@ impl Filler {
)*
}
};
#[cfg(all(feature = "log", feature = "nesting", not(feature = "simple-nesting")))]
let nesting_apply_section: TokenStream = if let Some(ref f) = default_log_fn {
quote! {
#(
let nesting_field_name = stringify!(#nesting_field_names);
self.#nesting_field_names.apply_with_log(filler.#nesting_field_names, |prefixes: &[&str], field: &str| {
let mut new_prefixes = struct_patch::__vec![nesting_field_name];
new_prefixes.extend_from_slice(prefixes);
#f(&new_prefixes , field);
});
)*
}
} else {
quote! {
#(
self.#nesting_field_names.apply(filler.#nesting_field_names);
)*
}
};
#[cfg(all(feature = "simple-nesting", not(feature = "log")))]
let nesting_apply_section: TokenStream = quote! {
#(
self.#nesting_field_names.apply(filler.#nesting_field_names);
)*
};
#[cfg(all(feature = "nesting", not(feature = "simple-nesting"), not(feature = "log")))]
let nesting_apply_section: TokenStream = quote! {
#(
self.#nesting_field_names.apply(filler.#nesting_field_names);
)*
};
#[cfg(not(feature = "nesting"))]
let nesting_apply_section: TokenStream = quote! {};

#[cfg(not(feature = "nesting"))]
#[cfg(all(feature = "log", not(feature = "nesting")))]
let apply_with_log_impl = quote! {
fn apply_with_log<__L: FnMut(&str)>(&mut self, filler: #name #generics, mut log: __L) {
#(
Expand All @@ -316,7 +355,37 @@ impl Filler {
)*
}
};
#[cfg(feature = "nesting")]
#[cfg(all(feature = "log", feature = "simple-nesting"))]
let apply_with_log_impl = quote! {
fn apply_with_log<__L: FnMut(&str, &str)>(&mut self, filler: #name #generics, mut log: __L) {
#(
if self.#native_value_field_names == #native_value_field_empty_values {
log("", stringify!(#native_value_field_names));
self.#native_value_field_names = filler.#native_value_field_names;
}
)*
#(
if self.#extendable_field_names.is_empty() {
log("", stringify!(#extendable_field_names));
self.#extendable_field_names.extend(filler.#extendable_field_names.into_iter());
}
)*
#(
if let Some(v) = filler.#option_field_names {
if self.#option_field_names.is_none() {
log("", stringify!(#option_field_names));
self.#option_field_names = Some(v);
}
}
)*
#(
self.#nesting_field_names.apply_with_log(filler.#nesting_field_names, |prefixes: &[&str], field: &str| {
log(stringify!(#nesting_field_names), field);
});
)*
}
};
#[cfg(all(feature = "log", feature = "nesting", not(feature = "simple-nesting")))]
let apply_with_log_impl = quote! {
fn apply_with_log<__L: FnMut(&[&str], &str)>(&mut self, filler: #name #generics, mut log: __L) {
#(
Expand All @@ -342,13 +411,15 @@ impl Filler {
#(
let nesting_field_name = stringify!(#nesting_field_names);
self.#nesting_field_names.apply_with_log(filler.#nesting_field_names, |prefixes: &[&str], field: &str| {
let mut new_prefixes = Vec::from(prefixes);
new_prefixes.push(nesting_field_name);
let mut new_prefixes = struct_patch::__vec![nesting_field_name];
new_prefixes.extend_from_slice(prefixes);
log(&new_prefixes, field);
});
)*
}
};
#[cfg(not(feature = "log"))]
let apply_with_log_impl = quote! {};

let filler_impl = quote! {
#[automatically_derived]
Expand Down Expand Up @@ -419,6 +490,7 @@ impl Filler {

let mut attributes = vec![];
let mut fields = vec![];
#[cfg(feature = "log")]
let mut default_log_fn: Option<syn::Path> = None;

for attr in attrs {
Expand All @@ -443,12 +515,17 @@ impl Filler {
let attribute: TokenStream = content.parse()?;
attributes.push(attribute);
}
#[cfg(feature = "log")]
DEFAULT_LOG => {
// #[filler(default_log(path::to::fn))]
let content;
parenthesized!(content in meta.input);
default_log_fn = Some(content.parse()?);
}
#[cfg(not(feature = "log"))]
DEFAULT_LOG => {
return Err(meta.error("`default_log` attribute requires the `log` feature"));
}
_ => {
return Err(meta.error(format_args!(
"unknown filler container attribute `{}`",
Expand Down Expand Up @@ -476,6 +553,7 @@ impl Filler {
generics,
attributes,
fields,
#[cfg(feature = "log")]
default_log_fn,
})
}
Expand Down
Loading
Loading