mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
Add Destruct
macro and trait for automatically acessing struct fields
This commit is contained in:
parent
33de539d6d
commit
4016653371
5 changed files with 106 additions and 8 deletions
|
@ -252,6 +252,11 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||
|
||||
let properties = &attributes.properties_string.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
|
||||
|
||||
let output_fields = match attributes.deconstruct_output {
|
||||
false => quote!(&[]),
|
||||
true => quote!(#output_type::fields),
|
||||
};
|
||||
|
||||
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier);
|
||||
Ok(quote! {
|
||||
/// Underlying implementation for [#struct_name]
|
||||
|
@ -310,6 +315,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||
category: #category,
|
||||
description: #description,
|
||||
properties: #properties,
|
||||
output_fields: #output_fields,
|
||||
fields: vec![
|
||||
#(
|
||||
FieldMetadata {
|
||||
|
@ -371,7 +377,7 @@ fn generate_node_input_references(parsed: &ParsedNodeFn, fn_generics: &[crate::G
|
|||
impl <#(#used),*> #graphene_core::NodeInputDecleration for #struct_name <#(#fn_generic_params),*> {
|
||||
const INDEX: usize = #input_index;
|
||||
fn identifier() -> &'static str {
|
||||
protonode_identifier()
|
||||
#identifier
|
||||
}
|
||||
type Result = #ty;
|
||||
}
|
||||
|
@ -381,12 +387,6 @@ fn generate_node_input_references(parsed: &ParsedNodeFn, fn_generics: &[crate::G
|
|||
quote! {
|
||||
pub mod #inputs_module_name {
|
||||
use super::*;
|
||||
|
||||
pub fn protonode_identifier() -> &'static str {
|
||||
// Storing the string in a once lock should reduce allocations (since we call this in a loop)?
|
||||
static NODE_NAME: std::sync::OnceLock<String> = std::sync::OnceLock::new();
|
||||
NODE_NAME.get_or_init(|| #identifier )
|
||||
}
|
||||
#(#generated_input_accessor)*
|
||||
}
|
||||
}
|
||||
|
|
54
node-graph/node-macro/src/destruct.rs
Normal file
54
node-graph/node-macro/src/destruct.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{Error, Ident, spanned::Spanned};
|
||||
|
||||
pub fn derive(struct_name: Ident, data: syn::Data) -> syn::Result<TokenStream2> {
|
||||
let syn::Data::Struct(data_struct) = data else {
|
||||
return Err(Error::new(proc_macro2::Span::call_site(), String::from("Deriving `Destruct` is currently only supported for structs")));
|
||||
};
|
||||
|
||||
let crate_name = proc_macro_crate::crate_name("graphene-core").map_err(|e| {
|
||||
Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
format!("Failed to find location of graphene_core. Make sure it is imported as a dependency: {}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
let path = quote!(std::module_path!().rsplit_once("::").unwrap().0);
|
||||
|
||||
let mut node_implementations = Vec::with_capacity(data_struct.fields.len());
|
||||
let mut field_structs = Vec::with_capacity(data_struct.fields.len());
|
||||
|
||||
for field in data_struct.fields {
|
||||
let Some(field_name) = field.ident else {
|
||||
return Err(Error::new(field.span(), String::from("Destruct cant be used on tuple structs")));
|
||||
};
|
||||
let ty = field.ty;
|
||||
let fn_name = quote::format_ident!("extract_ {field_name}");
|
||||
node_implementations.push(quote! {
|
||||
#[node_macro(category(""))]
|
||||
fn #fn_name(_: impl Ctx, data: #struct_name) -> #ty {
|
||||
data.#field_name
|
||||
}
|
||||
});
|
||||
|
||||
field_structs.push(quote! {
|
||||
#crate_name::registry::FieldStruct {
|
||||
name: stringify!(#field_name),
|
||||
node_path: concat!()
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
impl graphene_core::registry::Destruct for #struct_name {
|
||||
fn fields() -> &[graphene_core::registry::FieldStruct] {
|
||||
&[
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
|
@ -125,6 +125,21 @@ pub fn old_node_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
node_impl_proxy(attr, item)
|
||||
}
|
||||
|
||||
mod destruct;
|
||||
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_derive(Destruct)]
|
||||
/// Derives the `Destruct` trait for structs and creates acessor node implementations.
|
||||
pub fn derive_destruct(item: TokenStream) -> TokenStream {
|
||||
let s = syn::parse_macro_input!(item as syn::DeriveInput);
|
||||
let parse_result = destruct::derive(s.ident, s.data).into();
|
||||
let Ok(parsed_node) = parse_result else {
|
||||
let e = parse_result.unwrap_err();
|
||||
return syn::Error::new(e.span(), format!("Failed to parse node function: {e}")).to_compile_error().into();
|
||||
};
|
||||
parsed_node.into()
|
||||
}
|
||||
|
||||
fn node_new_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let node = parse_macro_input!(attr as syn::PathSegment);
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ pub(crate) struct NodeFnAttributes {
|
|||
pub(crate) path: Option<Path>,
|
||||
pub(crate) skip_impl: bool,
|
||||
pub(crate) properties_string: Option<LitStr>,
|
||||
pub(crate) deconstruct_output: bool,
|
||||
// Add more attributes as needed
|
||||
}
|
||||
|
||||
|
@ -173,6 +174,7 @@ impl Parse for NodeFnAttributes {
|
|||
let mut display_name = None;
|
||||
let mut path = None;
|
||||
let mut skip_impl = false;
|
||||
let mut deconstruct_output = false;
|
||||
let mut properties_string = None;
|
||||
|
||||
let content = input;
|
||||
|
@ -213,6 +215,12 @@ impl Parse for NodeFnAttributes {
|
|||
}
|
||||
skip_impl = true;
|
||||
}
|
||||
Meta::Path(path) if path.is_ident("deconstruct_output") => {
|
||||
if skip_impl {
|
||||
return Err(Error::new_spanned(path, "Multiple 'deconstruct_output' attributes are not allowed"));
|
||||
}
|
||||
deconstruct_output = true;
|
||||
}
|
||||
Meta::List(meta) if meta.path.is_ident("properties") => {
|
||||
if properties_string.is_some() {
|
||||
return Err(Error::new_spanned(path, "Multiple 'properties_string' attributes are not allowed"));
|
||||
|
@ -229,7 +237,7 @@ impl Parse for NodeFnAttributes {
|
|||
indoc!(
|
||||
r#"
|
||||
Unsupported attribute in `node`.
|
||||
Supported attributes are 'category', 'path' and 'name'.
|
||||
Supported attributes are 'category', 'path', 'skip_impl', 'deconstruct_output', 'properties_string' and 'name'.
|
||||
|
||||
Example usage:
|
||||
#[node_macro::node(category("Value"), name("Test Node"))]
|
||||
|
@ -246,6 +254,7 @@ impl Parse for NodeFnAttributes {
|
|||
path,
|
||||
skip_impl,
|
||||
properties_string,
|
||||
deconstruct_output,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue