mirror of
https://github.com/robbert-vdh/nih-plug.git
synced 2026-07-01 02:36:54 +00:00
Add parameter groups with #[nested = "Group Name"]
This commit is contained in:
@@ -29,10 +29,12 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
// JSON. The `nested` fields should also implement the `Params` trait and their fields will be
|
||||
// inherited and added to this field's lists.
|
||||
let mut param_mapping_insert_tokens = Vec::new();
|
||||
let mut param_groups_insert_tokens = Vec::new();
|
||||
let mut param_id_string_tokens = Vec::new();
|
||||
let mut field_serialize_tokens = Vec::new();
|
||||
let mut field_deserialize_tokens = Vec::new();
|
||||
let mut nested_fields_idents = Vec::new();
|
||||
let mut nested_params_field_idents: Vec<syn::Ident> = Vec::new();
|
||||
let mut nested_params_group_names: Vec<String> = Vec::new();
|
||||
|
||||
// We'll also enforce that there are no duplicate keys at compile time
|
||||
// TODO: This doesn't work for nested fields since we don't know anything about the fields on
|
||||
@@ -48,7 +50,8 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
// These two attributes are mutually exclusive
|
||||
let mut id_attr: Option<String> = None;
|
||||
let mut persist_attr: Option<String> = None;
|
||||
let mut nested = false;
|
||||
// And the `#[nested = "..."]` attribute contains a group name we should use
|
||||
let mut nested_attr: Option<String> = None;
|
||||
for attr in &field.attrs {
|
||||
if attr.path.is_ident("id") {
|
||||
match attr.parse_meta() {
|
||||
@@ -98,22 +101,34 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
} else if attr.path.is_ident("nested") {
|
||||
match attr.parse_meta() {
|
||||
Ok(syn::Meta::Path(_)) => {
|
||||
if !nested {
|
||||
nested = true;
|
||||
} else {
|
||||
Ok(syn::Meta::NameValue(syn::MetaNameValue {
|
||||
lit: syn::Lit::Str(s),
|
||||
..
|
||||
})) => {
|
||||
let s = s.value();
|
||||
if s.is_empty() {
|
||||
return syn::Error::new(attr.span(), "Group names cannot be empty")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
} else if s.contains('/') {
|
||||
return syn::Error::new(attr.span(), "Group names may not contain slashes")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
} else if nested_attr.is_some() {
|
||||
return syn::Error::new(attr.span(), "Duplicate nested attribute")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
} else {
|
||||
nested_attr = Some(s);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return syn::Error::new(
|
||||
attr.span(),
|
||||
"The nested attribute should not have any arguments: #[nested]",
|
||||
"The nested attribute should be a key-value pair with a string argument: #[nested = \"Group Name\"]",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
.into()
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -134,6 +149,10 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
// variant
|
||||
param_mapping_insert_tokens
|
||||
.push(quote! { param_map.insert(#param_id, self.#field_name.as_ptr()); });
|
||||
// Top-level parameters have no group, and we'll prefix the group name specified in
|
||||
// the `#[nested = "..."]` attribute to fields coming from nested groups
|
||||
param_groups_insert_tokens
|
||||
.push(quote! { param_groups.insert(#param_id, String::new()); });
|
||||
param_id_string_tokens.push(quote! { #param_id, });
|
||||
}
|
||||
(None, Some(stable_name)) => {
|
||||
@@ -193,8 +212,10 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
(None, None) => (),
|
||||
}
|
||||
|
||||
if nested {
|
||||
nested_fields_idents.push(field_name.clone());
|
||||
if let Some(nested_group_name) = nested_attr {
|
||||
nested_params_field_idents.push(field_name.clone());
|
||||
// FIXME: Generate the insertion code here
|
||||
nested_params_group_names.push(nested_group_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,25 +224,59 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
fn param_map(
|
||||
self: std::pin::Pin<&Self>,
|
||||
) -> std::collections::HashMap<&'static str, nih_plug::param::internals::ParamPtr> {
|
||||
// This may not be in scope otherwise
|
||||
// This may not be in scope otherwise, used to call .as_ptr()
|
||||
use ::nih_plug::param::Param;
|
||||
|
||||
let mut param_map = std::collections::HashMap::new();
|
||||
#(#param_mapping_insert_tokens)*
|
||||
|
||||
let nested_fields: &[&dyn Params] = &[#(&self.#nested_fields_idents),*];
|
||||
for nested_params in nested_fields {
|
||||
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
|
||||
for nested_params in nested_params_fields {
|
||||
unsafe { param_map.extend(Pin::new_unchecked(*nested_params).param_map()) };
|
||||
}
|
||||
|
||||
param_map
|
||||
}
|
||||
|
||||
fn param_groups(
|
||||
self: std::pin::Pin<&Self>,
|
||||
) -> std::collections::HashMap<&'static str, String> {
|
||||
let mut param_groups = std::collections::HashMap::new();
|
||||
#(#param_groups_insert_tokens)*
|
||||
|
||||
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
|
||||
let nested_params_groups: &[&'static str] = &[#(#nested_params_group_names),*];
|
||||
for (nested_params, group_name) in
|
||||
nested_params_fields.into_iter().zip(nested_params_groups)
|
||||
{
|
||||
let nested_param_groups =
|
||||
unsafe { std::pin::Pin::new_unchecked(*nested_params).param_groups() };
|
||||
let prefixed_nested_param_groups =
|
||||
nested_param_groups
|
||||
.into_iter()
|
||||
.map(|(param_id, nested_group_name)| {
|
||||
(
|
||||
param_id,
|
||||
if nested_group_name.is_empty() {
|
||||
group_name.to_string()
|
||||
} else {
|
||||
format!("{}/{}", group_name, nested_group_name)
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
param_groups.extend(prefixed_nested_param_groups);
|
||||
}
|
||||
|
||||
param_groups
|
||||
}
|
||||
|
||||
|
||||
fn param_ids(self: std::pin::Pin<&Self>) -> Vec<&'static str> {
|
||||
let mut ids = vec![#(#param_id_string_tokens)*];
|
||||
|
||||
let nested_fields: &[&dyn Params] = &[#(&self.#nested_fields_idents),*];
|
||||
for nested_params in nested_fields {
|
||||
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
|
||||
for nested_params in nested_params_fields {
|
||||
unsafe { ids.append(&mut Pin::new_unchecked(*nested_params).param_ids()) };
|
||||
}
|
||||
|
||||
@@ -232,8 +287,8 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
let mut serialized = ::std::collections::HashMap::new();
|
||||
#(#field_serialize_tokens)*
|
||||
|
||||
let nested_fields: &[&dyn Params] = &[#(&self.#nested_fields_idents),*];
|
||||
for nested_params in nested_fields {
|
||||
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
|
||||
for nested_params in nested_params_fields {
|
||||
unsafe { serialized.extend(Pin::new_unchecked(*nested_params).serialize_fields()) };
|
||||
}
|
||||
|
||||
@@ -252,8 +307,8 @@ pub fn derive_params(input: TokenStream) -> TokenStream {
|
||||
// parameter structs. An easy fix would be to use
|
||||
// https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.drain_filter
|
||||
// once that gets stabilized.
|
||||
let nested_fields: &[&dyn Params] = &[#(&self.#nested_fields_idents),*];
|
||||
for nested_params in nested_fields {
|
||||
let nested_params_fields: &[&dyn Params] = &[#(&self.#nested_params_field_idents),*];
|
||||
for nested_params in nested_params_fields {
|
||||
unsafe { Pin::new_unchecked(*nested_params).deserialize_fields(serialized) };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user