From 02b258ae1eefbcc613afbd9fab0f195401a00b35 Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 8 Jun 2026 15:11:16 +0200 Subject: feat(engine): add visibility reflection --- engine-macros/src/lib.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'engine-macros/src') diff --git a/engine-macros/src/lib.rs b/engine-macros/src/lib.rs index 9fe92e5..e7b3772 100644 --- a/engine-macros/src/lib.rs +++ b/engine-macros/src/lib.rs @@ -1,8 +1,19 @@ #![deny(clippy::all, clippy::pedantic)] +use std::fmt::Write; + use proc_macro::TokenStream; use quote::{format_ident, quote, ToTokens}; -use syn::{parse, Fields, Item, ItemEnum, ItemStruct, LitStr, Path as SynPath}; +use syn::{ + parse, + Fields, + Item, + ItemEnum, + ItemStruct, + LitStr, + Path as SynPath, + Visibility as SynVisibility, +}; macro_rules! syn_path { ($first_segment: ident $(::$segment: ident)*) => { @@ -58,6 +69,8 @@ fn generate_struct_reflection_impl(input: ItemStruct) -> TokenStream // since std::any::type_name as const is not stable yet let field_type_name = field_type.to_token_stream().to_string(); + let field_vis = gen_reflection_visibility_path(&field.vis, &engine_crate_path); + quote! { #engine_crate_path::reflection::StructField { name: #field_name, @@ -103,7 +116,8 @@ fn generate_struct_reflection_impl(input: ItemStruct) -> TokenStream (&SpecializationTarget::<#field_type>(std::marker::PhantomData)) .field_type_reflection() - }) + }), + visibility: #field_vis } } }); @@ -197,6 +211,48 @@ fn generate_enum_reflection_impl(input: ItemEnum) -> TokenStream .into() } +fn gen_reflection_visibility_path( + visibility: &SynVisibility, + engine_crate_path: &SynPath, +) -> proc_macro2::TokenStream +{ + match visibility { + SynVisibility::Public(_) => { + quote! { #engine_crate_path::reflection::Visibility::Pub } + } + SynVisibility::Restricted(vis_restricted) => { + let vis_scope = if vis_restricted.in_token.is_some() { + let in_path = syn_path_to_string(&vis_restricted.path); + + quote! { + #engine_crate_path::reflection::VisibilityScope::In( + std::borrow::Cow::Borrowed(#in_path) + ) + } + } else { + let Some(scope) = vis_restricted.path.get_ident() else { + unreachable!(); + }; + + if scope == "crate" { + quote! { #engine_crate_path::reflection::VisibilityScope::Crate } + } else if scope == "super" { + quote! { #engine_crate_path::reflection::VisibilityScope::Super } + } else if scope == "self" { + quote! { #engine_crate_path::reflection::VisibilityScope::SelfModule } + } else { + unreachable!(); + } + }; + + quote! { #engine_crate_path::reflection::Visibility::PubScoped(#vis_scope) } + } + SynVisibility::Inherited => { + quote! { #engine_crate_path::reflection::Visibility::Private } + } + } +} + fn find_engine_crate_path() -> Option { let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; @@ -213,3 +269,24 @@ fn find_engine_crate_path() -> Option Some(syn_path!(engine)) } + +fn syn_path_to_string(path: &syn::Path) -> String +{ + let mut output = String::with_capacity(2 + path.segments.len() * 8); + + if let Some(leading_colon) = path.leading_colon { + write!(output, "{}", leading_colon.to_token_stream()).unwrap(); + } + + for (segment, punct) in path.segments.pairs().map(syn::punctuated::Pair::into_tuple) { + let segment_ident = &segment.ident; + + write!(output, "{segment_ident}",).unwrap(); + + if let Some(punct) = punct { + write!(output, "{}", punct.to_token_stream()).unwrap(); + } + } + + output +} -- cgit v1.2.3-18-g5258