1
0
Fork 0

Preserve the order of non-grouped nested parameters

This commit is contained in:
Simon Leiner 2022-11-12 17:48:21 +01:00
parent d9797a606e
commit 4affa40244
No known key found for this signature in database
GPG key ID: AAAEDED27D405514
2 changed files with 63 additions and 19 deletions

View file

@ -31,9 +31,9 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
// keys at compile time. // keys at compile time.
// TODO: This duplication check doesn't work for nested fields since we don't know anything // TODO: This duplication check doesn't work for nested fields since we don't know anything
// about the fields on the nested structs // about the fields on the nested structs
let mut params: Vec<Param> = Vec::new(); let mut params: Vec<AnyParam> = Vec::new();
let mut persistent_fields: Vec<PersistentField> = Vec::new(); let mut persistent_fields: Vec<PersistentField> = Vec::new();
let mut nested_params: Vec<NestedParams> = Vec::new(); let mut group_params: Vec<NestedParams> = Vec::new();
for field in fields.named { for field in fields.named {
let field_name = match &field.ident { let field_name = match &field.ident {
Some(ident) => ident, Some(ident) => ident,
@ -62,7 +62,10 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
// This is a vector since we want to preserve the order. If structs get // This is a vector since we want to preserve the order. If structs get
// large enough to the point where a linear search starts being expensive, // large enough to the point where a linear search starts being expensive,
// then the plugin should probably start splitting up their parameters. // then the plugin should probably start splitting up their parameters.
if params.iter().any(|p| p.id == s) { if params.iter().any(|p| match p {
AnyParam::Single(param) => param.id == s,
_ => false,
}) {
return syn::Error::new( return syn::Error::new(
field.span(), field.span(),
"Multiple parameters with the same ID found", "Multiple parameters with the same ID found",
@ -71,10 +74,10 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
.into(); .into();
} }
params.push(Param { params.push(AnyParam::Single(Param {
id: s, id: s,
field: field_name.clone(), field: field_name.clone(),
}); }));
processed_attribute = true; processed_attribute = true;
} }
@ -204,7 +207,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
} }
} }
nested_params.push(match (nested_array, nested_id_prefix) { let param = match (nested_array, nested_id_prefix) {
(true, None) => NestedParams::Array { (true, None) => NestedParams::Array {
field: field_name.clone(), field: field_name.clone(),
group: nested_group, group: nested_group,
@ -226,7 +229,20 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
.to_compile_error() .to_compile_error()
.into() .into()
} }
}); };
// Grouped parameters are handled differently than nested parameters:
// DAWs that support grouped parameters usually display parameter groups below "regular" ones
// in a tree view. To keep the order consistent with those, group parameters are collected
// separately, so they can be inserted at the end of the parameter list.
// Nested parameters without a group on the other hand are merely an implementation detail of
// the plugin and thus should not behave different than regular parameters to the user. Because
// of this, they are inserted alongside the "regular" ones.
if param.is_group() {
group_params.push(param);
} else {
params.push(AnyParam::Nested(param));
}
processed_attribute = true; processed_attribute = true;
} }
@ -247,21 +263,17 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
// The next step is build the gathered information into tokens that can be spliced into a // The next step is build the gathered information into tokens that can be spliced into a
// `Params` implementation // `Params` implementation
let param_map_tokens = { let param_map_tokens = {
// `param_map` adds the parameters from this struct, and then handles the nested tokens. let param_mapping_self_tokens = params.into_iter().map(|p| p.to_token());
let param_mapping_self_tokens = params.into_iter().map( let param_mapping_group_tokens = group_params.iter().map(|p| p.to_token());
|Param {field, id}| quote! { (String::from(#id), self.#field.as_ptr(), String::new()) },
);
let param_mapping_nested_tokens = nested_params.iter().map(|p| p.to_token());
quote! { quote! {
// This may not be in scope otherwise, used to call .as_ptr() // This may not be in scope otherwise, used to call .as_ptr()
use ::nih_plug::params::Param; use ::nih_plug::params::Param;
#[allow(unused_mut)] #[allow(unused_mut)]
let mut param_map = vec![#(#param_mapping_self_tokens),*]; let mut param_map = Vec::new();
#(param_map.extend(#param_mapping_self_tokens); )*
#(param_map.extend(#param_mapping_nested_tokens); )* #(param_map.extend(#param_mapping_group_tokens); )*
param_map param_map
} }
@ -318,7 +330,7 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
.unzip(); .unzip();
let (serialize_fields_nested_tokens, deserialize_fields_nested_tokens): (Vec<_>, Vec<_>) = let (serialize_fields_nested_tokens, deserialize_fields_nested_tokens): (Vec<_>, Vec<_>) =
nested_params group_params
.iter() .iter()
.map(|nested| match nested { .map(|nested| match nested {
NestedParams::Inline { field, .. } | NestedParams::Prefixed { field, .. } => ( NestedParams::Inline { field, .. } | NestedParams::Prefixed { field, .. } => (
@ -390,6 +402,23 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
.into() .into()
} }
#[derive(Debug)]
enum AnyParam {
Single(Param),
Nested(NestedParams),
}
impl AnyParam {
fn to_token(&self) -> proc_macro2::TokenStream {
match self {
AnyParam::Single(Param { field, id }) => {
quote! { [(String::from(#id), self.#field.as_ptr(), String::new())] }
}
AnyParam::Nested(params) => params.to_token(),
}
}
}
/// A parameter that should be added to the parameter map. /// A parameter that should be added to the parameter map.
#[derive(Debug)] #[derive(Debug)]
struct Param { struct Param {
@ -434,6 +463,20 @@ enum NestedParams {
} }
impl NestedParams { impl NestedParams {
fn is_group(&self) -> bool {
let group = match self {
NestedParams::Inline { field: _, group } => group,
NestedParams::Prefixed {
field: _,
id_prefix: _,
group,
} => group,
NestedParams::Array { field: _, group } => group,
};
group.is_some()
}
fn to_token(&self) -> proc_macro2::TokenStream { fn to_token(&self) -> proc_macro2::TokenStream {
// How nested parameters are handled depends on the `NestedParams` variant. // How nested parameters are handled depends on the `NestedParams` variant.
// These are pairs of `(parameter_id, param_ptr, param_group)`. The specific // These are pairs of `(parameter_id, param_ptr, param_group)`. The specific

View file

@ -106,11 +106,12 @@ mod param_order {
fn nested() { fn nested() {
let p = NestedParams::default(); let p = NestedParams::default();
// Parameters must have the same order as they are defined in. Nested parameters are put in the end though. // Parameters must have the same order as they are defined in. The position of nested parameters which are not
// grouped explicitly is preserved.
let param_ids: Vec<String> = p.param_map().into_iter().map(|(id, _, _)| id).collect(); let param_ids: Vec<String> = p.param_map().into_iter().map(|(id, _, _)| id).collect();
assert_eq!( assert_eq!(
param_ids, param_ids,
["one", "three", "two_one", "two_two", "two_three"] ["one", "two_one", "two_two", "two_three", "three",]
); );
} }
} }