summaryrefslogtreecommitdiff
path: root/engine-macros
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-06-08 15:11:16 +0200
committerHampusM <hampus@hampusmat.com>2026-06-08 15:11:16 +0200
commit02b258ae1eefbcc613afbd9fab0f195401a00b35 (patch)
tree18b413e8575923b807155aa3477bd2155b08319f /engine-macros
parentca33e594edea5e17ec26c760f92804b4e7f653a7 (diff)
feat(engine): add visibility reflection
Diffstat (limited to 'engine-macros')
-rw-r--r--engine-macros/src/lib.rs81
1 files changed, 79 insertions, 2 deletions
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<SynPath>
{
let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
@@ -213,3 +269,24 @@ fn find_engine_crate_path() -> Option<SynPath>
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
+}