1
0
Fork 0

Support #[nested] prefixes/suffixes w/ #[persist]

This makes nested fields behave the same as nested parameters, allowing
multiple copies of a persistent field to exist.
This commit is contained in:
Robbert van der Helm 2022-11-19 00:17:13 +01:00
parent 5d9f1b109c
commit 753ca201a6
3 changed files with 59 additions and 19 deletions

View file

@ -8,6 +8,14 @@ code then it will not be listed here.
## [2022-11-17]
- The `Params` derive macro now also properly supports persistent fields in
`#[nested]` parameter structs. This takes `#[nested(id_prefix = "...")]` and
`#[nested(array)]` into account to allow multiple copies of a persistent
field. This may break existing usages as serialized field data without a
matching preffix or suffix is no longer passed to the child object.
## [2022-11-17]
- The order of `#[nested]` parameters in the parameter list now always follows
the declaration order instead of nested parameters being ordered below regular
parameters.

View file

@ -313,6 +313,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
})
.unzip();
// ID prefixes are also added for nested objects
let (serialize_fields_nested_tokens, deserialize_fields_nested_tokens): (Vec<_>, Vec<_>) =
params
.iter()
@ -321,23 +322,60 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
Param::Nested(nested) => Some(nested),
})
.map(|nested| match nested {
NestedParams::Inline { field, .. } | NestedParams::Prefixed { field, .. } => (
// TODO: For some reason the macro won't parse correctly if you inline this
NestedParams::Inline { field, .. } => (
quote! { serialized.extend(self.#field.serialize_fields()); },
quote! { self.#field.deserialize_fields(serialized); },
),
NestedParams::Prefixed {
field, id_prefix, ..
} => (
quote! {
let inlineme = self.#field.serialize_fields();
serialized.extend(inlineme);
let prefixed = self
.#field
.serialize_fields()
.into_iter()
.map(|(key, value)| (format!("{}_{}", #id_prefix, key), value));
serialized.extend(prefixed);
},
quote! {
let prefix = format!("{}_", #id_prefix);
let matching_fields = serialized
.iter()
.filter_map(|(key, value)| {
let original_key = key.strip_prefix(&prefix)?;
Some((original_key.to_owned(), value.to_owned()))
})
.collect();
self.#field.deserialize_fields(&matching_fields);
},
quote! { self.#field.deserialize_fields(serialized) },
),
NestedParams::Array { field, .. } => (
quote! {
for field in self.#field.iter() {
serialized.extend(field.serialize_fields());
for (field_idx, field) in self.#field.iter().enumerate() {
let idx = field_idx + 1;
let suffixed = field
.serialize_fields()
.into_iter()
.map(|(key, value)| (format!("{}_{}", key, idx), value));
serialized.extend(suffixed);
}
},
quote! {
for field in self.#field.iter() {
field.deserialize_fields(serialized);
for (field_idx, field) in self.#field.iter().enumerate() {
let idx = field_idx + 1;
let suffix = format!("_{}", idx);
let matching_fields = serialized
.iter()
.filter_map(|(key, value)| {
let original_key = key.strip_suffix(&suffix)?;
Some((original_key.to_owned(), value.to_owned()))
})
.collect();
field.deserialize_fields(&matching_fields);
}
},
),

View file

@ -230,10 +230,7 @@ pub(crate) trait ParamMut: Param {
/// Finally, the `Params` object may include parameters from other objects. Setting a group name is
/// optional, but some hosts can use this information to display the parameters in a tree structure.
/// Parameter IDs and persisting keys still need to be **unique** when using nested parameter
/// structs. This currently has the following caveats:
///
/// - Enforcing that parameter IDs and persist keys are unique does not work across nested structs.
/// - Deserializing persisted fields will give false positives about fields not existing.
/// structs.
///
/// Take a look at the example gain example plugin to see how this is used.
///
@ -242,20 +239,17 @@ pub(crate) trait ParamMut: Param {
/// Adding this attribute to a `Params` sub-object works similarly to the regular `#[nested]`
/// attribute, but it also adds an ID to all parameters from the nested object. If a parameter in
/// the nested nested object normally has parameter ID `bar`, the parameter's ID will now be renamed
/// to `foo_bar`. _This makes it possible to reuse same parameter struct with different names and
/// to `foo_bar`. The same thing happens with persistent field keys to support multiple copies of
/// the field. _This makes it possible to reuse the same parameter struct with different names and
/// parameter indices._
///
/// This does **not** support persistent fields.
///
/// ## `#[nested(array, group_name = "Foo")]`
///
/// This can be applied to an array-like data structure and it works similar to a `nested` attribute
/// with an `id_name`, except that it will iterate over the array and create unique indices for all
/// nested parameters. If the nested parameters object has a parameter called `bar`, then that
/// parameter will belong to the group `Foo {array_index + 1}`, and it will have the renamed
/// parameter ID `bar_{array_index + 1}`.
///
/// This does **not** support persistent fields.
/// parameter ID `bar_{array_index + 1}`. The same thing applies to persistent field keys.
///
/// # Safety
///