mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
174 lines
5.9 KiB
Rust
174 lines
5.9 KiB
Rust
use super::*;
|
|
|
|
/// Generates strongly typed utilites to access inputs
|
|
pub(crate) fn generate_node_input_references(
|
|
parsed: &ParsedNodeFn,
|
|
fn_generics: &[crate::GenericParam],
|
|
field_idents: &[&PatIdent],
|
|
core_types: &TokenStream2,
|
|
identifier: &Ident,
|
|
cfg: &TokenStream2,
|
|
) -> TokenStream2 {
|
|
let inputs_module_name = format_ident!("{}", parsed.struct_name.to_string().to_case(Case::Snake));
|
|
|
|
let mut generated_input_accessor = Vec::new();
|
|
if !parsed.attributes.skip_impl {
|
|
let (mut modified, mut generic_collector) = FilterUsedGenerics::new(fn_generics);
|
|
|
|
for (input_index, (parsed_input, input_ident)) in parsed.fields.iter().zip(field_idents).enumerate() {
|
|
let mut ty = match &parsed_input.ty {
|
|
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => ty,
|
|
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => output_type,
|
|
}
|
|
.clone();
|
|
|
|
// We only want the necessary generics.
|
|
let used = generic_collector.filter_unnecessary_generics(&mut modified, &mut ty);
|
|
// TODO: figure out a better name that doesn't conflict with so many types
|
|
let struct_name = format_ident!("{}Input", input_ident.ident.to_string().to_case(Case::Pascal));
|
|
let (fn_generic_params, phantom_data_declerations) = generate_phantom_data(used.iter());
|
|
|
|
// Only create structs with phantom data where necessary.
|
|
generated_input_accessor.push(if phantom_data_declerations.is_empty() {
|
|
quote! {
|
|
pub struct #struct_name;
|
|
}
|
|
} else {
|
|
quote! {
|
|
pub struct #struct_name <#(#used),*>{
|
|
#(#phantom_data_declerations,)*
|
|
}
|
|
}
|
|
});
|
|
generated_input_accessor.push(quote! {
|
|
impl <#(#used),*> #core_types::NodeInputDecleration for #struct_name <#(#fn_generic_params),*> {
|
|
const INDEX: usize = #input_index;
|
|
fn identifier() -> #core_types::ProtoNodeIdentifier {
|
|
#inputs_module_name::IDENTIFIER.clone()
|
|
}
|
|
type Result = #ty;
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
quote! {
|
|
#cfg
|
|
pub mod #inputs_module_name {
|
|
use super::*;
|
|
|
|
/// The `ProtoNodeIdentifier` of this node without any generics attached to it
|
|
pub const IDENTIFIER: #core_types::ProtoNodeIdentifier = #identifier();
|
|
#(#generated_input_accessor)*
|
|
}
|
|
}
|
|
}
|
|
|
|
/// It is necessary to generate PhantomData for each fn generic to avoid compiler errors.
|
|
pub(crate) fn generate_phantom_data<'a>(fn_generics: impl Iterator<Item = &'a crate::GenericParam>) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
|
|
let mut phantom_data_declerations = Vec::new();
|
|
let mut fn_generic_params = Vec::new();
|
|
|
|
for fn_generic_param in fn_generics {
|
|
let field_name = format_ident!("phantom_{}", phantom_data_declerations.len());
|
|
|
|
match fn_generic_param {
|
|
crate::GenericParam::Lifetime(lifetime_param) => {
|
|
let lifetime = &lifetime_param.lifetime;
|
|
|
|
fn_generic_params.push(quote! {#lifetime});
|
|
phantom_data_declerations.push(quote! {#field_name: core::marker::PhantomData<&#lifetime ()>})
|
|
}
|
|
crate::GenericParam::Type(type_param) => {
|
|
let generic_name = &type_param.ident;
|
|
|
|
fn_generic_params.push(quote! {#generic_name});
|
|
phantom_data_declerations.push(quote! {#field_name: core::marker::PhantomData<#generic_name>});
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
(fn_generic_params, phantom_data_declerations)
|
|
}
|
|
|
|
|
|
/// Get only the necessary generics.
|
|
struct FilterUsedGenerics {
|
|
all: Vec<crate::GenericParam>,
|
|
used: Vec<bool>,
|
|
}
|
|
|
|
impl VisitMut for FilterUsedGenerics {
|
|
fn visit_lifetime_mut(&mut self, used_lifetime: &mut Lifetime) {
|
|
for (generic, used) in self.all.iter().zip(self.used.iter_mut()) {
|
|
let crate::GenericParam::Lifetime(lifetime_param) = generic else { continue };
|
|
if used_lifetime == &lifetime_param.lifetime {
|
|
*used = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn visit_path_mut(&mut self, path: &mut syn::Path) {
|
|
for (index, (generic, used)) in self.all.iter().zip(self.used.iter_mut()).enumerate() {
|
|
let crate::GenericParam::Type(type_param) = generic else { continue };
|
|
if path.leading_colon.is_none() && !path.segments.is_empty() && path.segments[0].arguments.is_none() && path.segments[0].ident == type_param.ident {
|
|
*used = true;
|
|
// Sometimes the generics conflict with the type name so we rename the generics.
|
|
path.segments[0].ident = format_ident!("G{index}");
|
|
}
|
|
}
|
|
for mut el in Punctuated::pairs_mut(&mut path.segments) {
|
|
self.visit_path_segment_mut(el.value_mut());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FilterUsedGenerics {
|
|
fn new(fn_generics: &[crate::GenericParam]) -> (Vec<crate::GenericParam>, Self) {
|
|
let mut all_possible_generics = fn_generics.to_vec();
|
|
// The 'n lifetime may also be needed; we must add it in
|
|
all_possible_generics.insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(Lifetime::new("'n", proc_macro2::Span::call_site()))));
|
|
|
|
let modified = all_possible_generics
|
|
.iter()
|
|
.cloned()
|
|
.enumerate()
|
|
.map(|(index, mut generic)| {
|
|
let crate::GenericParam::Type(type_param) = &mut generic else { return generic };
|
|
// Sometimes the generics conflict with the type name so we rename the generics.
|
|
type_param.ident = format_ident!("G{index}");
|
|
generic
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let generic_collector = Self {
|
|
used: vec![false; all_possible_generics.len()],
|
|
all: all_possible_generics,
|
|
};
|
|
|
|
(modified, generic_collector)
|
|
}
|
|
|
|
fn used<'a>(&'a self, modified: &'a [crate::GenericParam]) -> impl Iterator<Item = &'a crate::GenericParam> {
|
|
modified.iter().zip(&self.used).filter(|(_, used)| **used).map(move |(value, _)| value)
|
|
}
|
|
|
|
fn filter_unnecessary_generics(&mut self, modified: &mut Vec<syn::GenericParam>, ty: &mut Type) -> Vec<syn::GenericParam> {
|
|
self.used.fill(false);
|
|
|
|
// Find out which generics are necessary to support the node input
|
|
self.visit_type_mut(ty);
|
|
|
|
// Sometimes generics may reference other generics. This is a non-optimal way of dealing with that.
|
|
for _ in 0..=self.all.len() {
|
|
for (index, item) in modified.iter_mut().enumerate() {
|
|
if self.used[index] {
|
|
self.visit_generic_param_mut(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.used(&*modified).cloned().collect()
|
|
}
|
|
}
|