Skip to content

Commit

Permalink
Macroize ToNamedInputs and ToNamedOutputs
Browse files Browse the repository at this point in the history
  • Loading branch information
ahicks92 committed Nov 4, 2023
1 parent a3151e6 commit 4486f15
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 22 deletions.
7 changes: 1 addition & 6 deletions crates/synthizer/src/nodes/audio_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ use crate::internal_object_handle::InternalObjectHandle;
use crate::server::Server;
use crate::unique_id::UniqueId;

#[derive(synthizer_macros_internal::ToNamedInputs)]
pub(crate) struct Inputs<'a> {
input: &'a [AllocatedBlock],
}

impl<'a> ToNamedInputs<'a> for Inputs<'a> {
fn to_named_inputs<'b>(inputs: &'b mut InputsByIndex<'a>) -> Self {
Inputs { input: inputs[0] }
}
}

pub(crate) struct AudioOutputNodeAt {
format: ChannelFormat,
props: (),
Expand Down
12 changes: 1 addition & 11 deletions crates/synthizer/src/nodes/trig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,11 @@ pub struct TrigWaveformSlots {
pub(super) frequency: Slot<F64X1>,
}

#[derive(synthizer_macros_internal::ToNamedOutputs)]
pub(crate) struct TrigWaveformOutputs<'a> {
output: OutputDestination<'a>,
}

// TODO: macro this.
impl<'a> ToNamedOutputs<'a> for TrigWaveformOutputs<'a> {
fn to_named_outputs<'b>(
outputs: &'b mut crate::nodes::OutputsByIndex<'a>,
) -> TrigWaveformOutputs<'a> {
TrigWaveformOutputs {
output: outputs.pop_at(0).unwrap(),
}
}
}

impl HasNodeDescriptor for TrigWaveformNodeAt {
type Outputs<'a> = TrigWaveformOutputs<'a>;
type Inputs<'a> = ();
Expand Down
8 changes: 3 additions & 5 deletions crates/synthizer/src/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,9 @@ impl<V: PropertyKind> Slot<V> {

/// Try to set this property from a [PropertyValue] and panic if this was not possible.
pub(crate) fn set_from_property_value(&mut self, val: PropertyValue, why: ChangeState) {
let Ok(val): Result<V,_> = val
.try_into()
else {
panic!("Internal error: a property value of the wrong type reached a property slot");
};
let Ok(val): Result<V, _> = val.try_into() else {
panic!("Internal error: a property value of the wrong type reached a property slot");
};

self.data = val.extract();
self.change_state = why;
Expand Down
126 changes: 126 additions & 0 deletions crates/synthizer_macros_internal/src/derive_named_inputs_outputs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//! Implementation of derives for ToNamedInputs and ToNamedOutputs.
//!
//! These share a lot of code because they are identical trait bodies, just with different names for the functions.
use darling::FromDeriveInput;
use proc_macro_error::abort;

#[derive(FromDeriveInput)]
#[darling(supports(struct_named))]
struct NamedArgs {
ident: syn::Ident,
data: darling::ast::Data<(), syn::Field>,
}

/// Parameters for performing the generation of whichever case we are generating.
struct Opts {
/// Full path to the trait.
trait_path: syn::Path,

/// Name of the function.
func_name: syn::Ident,

/// Name of the argument to the function.
arg_name: syn::Ident,

/// Type of the argument, pointing at nodes::traits::BYIndex.
arg_ty_path: syn::Path,
}

/// Punch out either named inputs or outputs.
fn punchout(opts: Opts, tokens: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let derive_input: syn::DeriveInput = match syn::parse2(tokens) {
Ok(x) => x,
Err(e) => {
let span = e.span();
abort!(span, e);
}
};

let input = match NamedArgs::from_derive_input(&derive_input) {
Ok(x) => x,
Err(e) => {
let span = e.span();
abort!(span, e);
}
};

let Opts {
arg_name,
arg_ty_path,
func_name,
trait_path,
} = opts;

let struct_ident = input.ident;

// This is validated by darling already.
let fields = input
.data
.take_struct()
.unwrap()
.fields
.into_iter()
.map(|x| x.ident.unwrap())
.collect::<Vec<_>>();
if fields.is_empty() {
proc_macro_error::abort_call_site!("This struct must have at least one field. If you are trying to have no inputs or outputs, use `()` here instead.");
}

// We want to avoid continually shifting vectors forward. To do that, we will reverse the fields and pop from the
// back of the vector.
let mut fields_rev = fields;
fields_rev.reverse();

quote::quote!(
impl<'a> #trait_path<'a> for #struct_ident<'a> {
fn #func_name<'b>(#arg_name: &'b mut #arg_ty_path<'a>) -> #struct_ident<'a> {
#struct_ident {
#(
#fields_rev: #arg_name
.pop()
.expect("Mismatch between the node descriptor and the number of fields in this struct")
),*
}
}
}
)
}

fn ident_cs(name: &str) -> syn::Ident {
syn::Ident::new(name, proc_macro2::Span::call_site())
}

fn path_cs(path: &str) -> syn::Path {
use std::str::FromStr;
syn::parse2(proc_macro2::TokenStream::from_str(path).unwrap()).unwrap()
}

pub(crate) fn derive_to_named_outputs_impl(
tokens: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
punchout(
Opts {
arg_name: ident_cs("outputs"),
arg_ty_path: path_cs("crate::nodes::OutputsByIndex"),
func_name: ident_cs("to_named_outputs"),
trait_path: path_cs("crate::nodes::traits::ToNamedOutputs"),
},
tokens.into(),
)
.into()
}

pub(crate) fn derive_to_named_inputs_impl(
tokens: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
punchout(
Opts {
arg_name: ident_cs("inputs"),
arg_ty_path: path_cs("crate::nodes::InputsByIndex"),
func_name: ident_cs("to_named_inputs"),
trait_path: path_cs("crate::nodes::traits::ToNamedInputs"),
},
tokens.into(),
)
.into()
}
13 changes: 13 additions & 0 deletions crates/synthizer_macros_internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//!
//! This crate contains dummy forwarders at the root to let us split it into files. See the individual modules for docs
//! on the macro. Procmacro limitations currently require that they be at the root.
mod derive_named_inputs_outputs;
mod property_slots_impl;
mod utils;

Expand All @@ -19,3 +20,15 @@ pub fn property_slots(
) -> proc_macro::TokenStream {
property_slots_impl::property_slots_impl(attrs.into(), body.into()).into()
}

#[proc_macro_derive(ToNamedInputs)]
#[proc_macro_error::proc_macro_error]
pub fn to_named_inputs(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
crate::derive_named_inputs_outputs::derive_to_named_inputs_impl(tokens)
}

#[proc_macro_derive(ToNamedOutputs)]
#[proc_macro_error::proc_macro_error]
pub fn to_named_outputs(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
crate::derive_named_inputs_outputs::derive_to_named_outputs_impl(tokens)
}

0 comments on commit 4486f15

Please sign in to comment.