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] ## [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 order of `#[nested]` parameters in the parameter list now always follows
the declaration order instead of nested parameters being ordered below regular the declaration order instead of nested parameters being ordered below regular
parameters. parameters.

View file

@ -313,6 +313,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
}) })
.unzip(); .unzip();
// ID prefixes are also added for nested objects
let (serialize_fields_nested_tokens, deserialize_fields_nested_tokens): (Vec<_>, Vec<_>) = let (serialize_fields_nested_tokens, deserialize_fields_nested_tokens): (Vec<_>, Vec<_>) =
params params
.iter() .iter()
@ -321,23 +322,60 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
Param::Nested(nested) => Some(nested), Param::Nested(nested) => Some(nested),
}) })
.map(|nested| match nested { .map(|nested| match nested {
NestedParams::Inline { field, .. } | NestedParams::Prefixed { field, .. } => ( NestedParams::Inline { field, .. } => (
// TODO: For some reason the macro won't parse correctly if you inline this quote! { serialized.extend(self.#field.serialize_fields()); },
quote! { self.#field.deserialize_fields(serialized); },
),
NestedParams::Prefixed {
field, id_prefix, ..
} => (
quote! { quote! {
let inlineme = self.#field.serialize_fields(); let prefixed = self
serialized.extend(inlineme); .#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, .. } => ( NestedParams::Array { field, .. } => (
quote! { quote! {
for field in self.#field.iter() { for (field_idx, field) in self.#field.iter().enumerate() {
serialized.extend(field.serialize_fields()); let idx = field_idx + 1;
let suffixed = field
.serialize_fields()
.into_iter()
.map(|(key, value)| (format!("{}_{}", key, idx), value));
serialized.extend(suffixed);
} }
}, },
quote! { quote! {
for field in self.#field.iter() { for (field_idx, field) in self.#field.iter().enumerate() {
field.deserialize_fields(serialized); 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 /// 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. /// 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 /// Parameter IDs and persisting keys still need to be **unique** when using nested parameter
/// structs. This currently has the following caveats: /// structs.
///
/// - 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.
/// ///
/// Take a look at the example gain example plugin to see how this is used. /// 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]` /// 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 /// 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 /// 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._ /// parameter indices._
/// ///
/// This does **not** support persistent fields.
///
/// ## `#[nested(array, group_name = "Foo")]` /// ## `#[nested(array, group_name = "Foo")]`
/// ///
/// This can be applied to an array-like data structure and it works similar to a `nested` attribute /// 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 /// 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 /// 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 will belong to the group `Foo {array_index + 1}`, and it will have the renamed
/// parameter ID `bar_{array_index + 1}`. /// parameter ID `bar_{array_index + 1}`. The same thing applies to persistent field keys.
///
/// This does **not** support persistent fields.
/// ///
/// # Safety /// # Safety
/// ///