bevy_reflect: Reflect doc comments (#6234)
# Objective Resolves #6197 Make it so that doc comments can be retrieved via reflection. ## Solution Adds the new `documentation` feature to `bevy_reflect` (disabled by default). When enabled, documentation can be found using `TypeInfo::doc` for reflected types: ```rust /// Some struct. /// /// # Example /// /// ```ignore /// let some_struct = SomeStruct; /// ``` #[derive(Reflect)] struct SomeStruct; let info = <SomeStruct as Typed>::type_info(); assert_eq!( Some(" Some struct.\n\n # Example\n\n ```ignore\n let some_struct = SomeStruct;\n ```"), info.docs() ); ``` ### Notes for Reviewers The bulk of the files simply added the same 16 lines of code (with slightly different documentation). Most of the real changes occur in the `bevy_reflect_derive` files as well as in the added tests. --- ## Changelog * Added `documentation` feature to `bevy_reflect` * Added `TypeInfo::docs` method (and similar methods for all info types)
This commit is contained in:
parent
4407cdb423
commit
a658bfef19
@ -10,7 +10,11 @@ keywords = ["bevy"]
|
||||
readme = "README.md"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
# Provides Bevy-related reflection implementations
|
||||
bevy = ["glam", "smallvec", "bevy_math"]
|
||||
# When enabled, allows documentation comments to be accessed via reflection
|
||||
documentation = ["bevy_reflect_derive/documentation"]
|
||||
|
||||
[dependencies]
|
||||
# bevy
|
||||
@ -31,3 +35,8 @@ glam = { version = "0.21", features = ["serde"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ron = "0.8.0"
|
||||
|
||||
[[example]]
|
||||
name = "reflect_docs"
|
||||
path = "examples/reflect_docs.rs"
|
||||
required-features = ["documentation"]
|
||||
|
||||
@ -11,6 +11,11 @@ keywords = ["bevy"]
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
# When enabled, allows documentation comments to be processed by the reflection macros
|
||||
documentation = []
|
||||
|
||||
[dependencies]
|
||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0-dev" }
|
||||
|
||||
|
||||
@ -39,6 +39,9 @@ pub(crate) struct ReflectMeta<'a> {
|
||||
generics: &'a Generics,
|
||||
/// A cached instance of the path to the `bevy_reflect` crate.
|
||||
bevy_reflect_path: Path,
|
||||
/// The documentation for this type, if any
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: crate::documentation::Documentation,
|
||||
}
|
||||
|
||||
/// Struct data used by derive macros for `Reflect` and `FromReflect`.
|
||||
@ -86,6 +89,9 @@ pub(crate) struct StructField<'a> {
|
||||
pub attrs: ReflectFieldAttr,
|
||||
/// The index of this field within the struct.
|
||||
pub index: usize,
|
||||
/// The documentation for this field, if any
|
||||
#[cfg(feature = "documentation")]
|
||||
pub doc: crate::documentation::Documentation,
|
||||
}
|
||||
|
||||
/// Represents a variant on an enum.
|
||||
@ -100,6 +106,9 @@ pub(crate) struct EnumVariant<'a> {
|
||||
/// The index of this variant within the enum.
|
||||
#[allow(dead_code)]
|
||||
pub index: usize,
|
||||
/// The documentation for this variant, if any
|
||||
#[cfg(feature = "documentation")]
|
||||
pub doc: crate::documentation::Documentation,
|
||||
}
|
||||
|
||||
pub(crate) enum EnumVariantFields<'a> {
|
||||
@ -123,6 +132,9 @@ impl<'a> ReflectDerive<'a> {
|
||||
// Should indicate whether `#[reflect_value]` was used
|
||||
let mut reflect_mode = None;
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let mut doc = crate::documentation::Documentation::default();
|
||||
|
||||
for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
|
||||
match attribute {
|
||||
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
|
||||
@ -159,25 +171,31 @@ impl<'a> ReflectDerive<'a> {
|
||||
|
||||
reflect_mode = Some(ReflectMode::Value);
|
||||
}
|
||||
#[cfg(feature = "documentation")]
|
||||
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
|
||||
if let syn::Lit::Str(lit) = pair.lit {
|
||||
doc.push(lit.value());
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let meta = meta.with_docs(doc);
|
||||
|
||||
// Use normal reflection if unspecified
|
||||
let reflect_mode = reflect_mode.unwrap_or(ReflectMode::Normal);
|
||||
|
||||
if reflect_mode == ReflectMode::Value {
|
||||
return Ok(Self::Value(ReflectMeta::new(
|
||||
&input.ident,
|
||||
&input.generics,
|
||||
traits,
|
||||
)));
|
||||
return Ok(Self::Value(meta));
|
||||
}
|
||||
|
||||
return match &input.data {
|
||||
Data::Struct(data) => {
|
||||
let fields = Self::collect_struct_fields(&data.fields)?;
|
||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
||||
let reflect_struct = ReflectStruct {
|
||||
meta,
|
||||
serialization_denylist: members_to_serialization_denylist(
|
||||
@ -194,7 +212,6 @@ impl<'a> ReflectDerive<'a> {
|
||||
}
|
||||
Data::Enum(data) => {
|
||||
let variants = Self::collect_enum_variants(&data.variants)?;
|
||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
||||
|
||||
let reflect_enum = ReflectEnum { meta, variants };
|
||||
Ok(Self::Enum(reflect_enum))
|
||||
@ -216,6 +233,8 @@ impl<'a> ReflectDerive<'a> {
|
||||
index,
|
||||
attrs,
|
||||
data: field,
|
||||
#[cfg(feature = "documentation")]
|
||||
doc: crate::documentation::Documentation::from_attributes(&field.attrs),
|
||||
})
|
||||
})
|
||||
.fold(
|
||||
@ -245,6 +264,8 @@ impl<'a> ReflectDerive<'a> {
|
||||
attrs: parse_field_attrs(&variant.attrs)?,
|
||||
data: variant,
|
||||
index,
|
||||
#[cfg(feature = "documentation")]
|
||||
doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
|
||||
})
|
||||
})
|
||||
.fold(
|
||||
@ -263,9 +284,17 @@ impl<'a> ReflectMeta<'a> {
|
||||
type_name,
|
||||
generics,
|
||||
bevy_reflect_path: utility::get_bevy_reflect_path(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the documentation for this type.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The registered reflect traits on this struct.
|
||||
pub fn traits(&self) -> &ReflectTraits {
|
||||
&self.traits
|
||||
@ -296,6 +325,12 @@ impl<'a> ReflectMeta<'a> {
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// The collection of docstrings for this type, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn doc(&self) -> &crate::documentation::Documentation {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ReflectStruct<'a> {
|
||||
|
||||
77
crates/bevy_reflect/bevy_reflect_derive/src/documentation.rs
Normal file
77
crates/bevy_reflect/bevy_reflect_derive/src/documentation.rs
Normal file
@ -0,0 +1,77 @@
|
||||
//! Contains code related to documentation reflection (requires the `documentation` feature).
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{Attribute, Lit, Meta};
|
||||
|
||||
/// A struct used to represent a type's documentation, if any.
|
||||
///
|
||||
/// When converted to a [`TokenStream`], this will output an `Option<String>`
|
||||
/// containing the collection of doc comments.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Documentation {
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
impl Documentation {
|
||||
/// Create a new [`Documentation`] from a type's attributes.
|
||||
///
|
||||
/// This will collect all `#[doc = "..."]` attributes, including the ones generated via `///` and `//!`.
|
||||
pub fn from_attributes<'a>(attributes: impl IntoIterator<Item = &'a Attribute>) -> Self {
|
||||
let docs = attributes
|
||||
.into_iter()
|
||||
.filter_map(|attr| {
|
||||
let meta = attr.parse_meta().ok()?;
|
||||
match meta {
|
||||
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
|
||||
if let Lit::Str(lit) = pair.lit {
|
||||
Some(lit.value())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { docs }
|
||||
}
|
||||
|
||||
/// The full docstring, if any.
|
||||
pub fn doc_string(&self) -> Option<String> {
|
||||
if self.docs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let len = self.docs.len();
|
||||
Some(
|
||||
self.docs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, doc)| {
|
||||
if index < len - 1 {
|
||||
format!("{}\n", doc)
|
||||
} else {
|
||||
doc.to_owned()
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Push a new docstring to the collection
|
||||
pub fn push(&mut self, doc: String) {
|
||||
self.docs.push(doc);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Documentation {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
if let Some(doc) = self.doc_string() {
|
||||
quote!(Some(#doc)).to_tokens(tokens);
|
||||
} else {
|
||||
quote!(None).to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField};
|
||||
use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructField};
|
||||
use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors};
|
||||
use crate::impls::impl_typed;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::Fields;
|
||||
|
||||
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
||||
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
|
||||
@ -55,12 +56,28 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
||||
});
|
||||
|
||||
let string_name = enum_name.to_string();
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let info_generator = {
|
||||
let doc = reflect_enum.meta().doc();
|
||||
quote! {
|
||||
#bevy_reflect_path::EnumInfo::new::<Self>(#string_name, &variants).with_docs(#doc)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let info_generator = {
|
||||
quote! {
|
||||
#bevy_reflect_path::EnumInfo::new::<Self>(#string_name, &variants)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_impl = impl_typed(
|
||||
enum_name,
|
||||
reflect_enum.meta().generics(),
|
||||
quote! {
|
||||
let variants = [#(#variant_info),*];
|
||||
let info = #bevy_reflect_path::EnumInfo::new::<Self>(#string_name, &variants);
|
||||
let info = #info_generator;
|
||||
#bevy_reflect_path::TypeInfo::Enum(info)
|
||||
},
|
||||
bevy_reflect_path,
|
||||
@ -286,6 +303,18 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
||||
let name = ident.to_string();
|
||||
let unit = reflect_enum.get_unit(ident);
|
||||
|
||||
let variant_type_ident = match variant.data.fields {
|
||||
Fields::Unit => Ident::new("Unit", Span::call_site()),
|
||||
Fields::Unnamed(..) => Ident::new("Tuple", Span::call_site()),
|
||||
Fields::Named(..) => Ident::new("Struct", Span::call_site()),
|
||||
};
|
||||
|
||||
let variant_info_ident = match variant.data.fields {
|
||||
Fields::Unit => Ident::new("UnitVariantInfo", Span::call_site()),
|
||||
Fields::Unnamed(..) => Ident::new("TupleVariantInfo", Span::call_site()),
|
||||
Fields::Named(..) => Ident::new("StructVariantInfo", Span::call_site()),
|
||||
};
|
||||
|
||||
enum_variant_name.push(quote! {
|
||||
#unit{..} => #name
|
||||
});
|
||||
@ -293,10 +322,10 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
||||
#unit{..} => #variant_index
|
||||
});
|
||||
|
||||
fn for_fields(
|
||||
fn get_field_args(
|
||||
fields: &[StructField],
|
||||
mut generate_for_field: impl FnMut(usize, usize, &StructField) -> proc_macro2::TokenStream,
|
||||
) -> (usize, Vec<proc_macro2::TokenStream>) {
|
||||
) -> Vec<proc_macro2::TokenStream> {
|
||||
let mut constructor_argument = Vec::new();
|
||||
let mut reflect_idx = 0;
|
||||
for field in fields.iter() {
|
||||
@ -307,41 +336,64 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
||||
constructor_argument.push(generate_for_field(reflect_idx, field.index, field));
|
||||
reflect_idx += 1;
|
||||
}
|
||||
(reflect_idx, constructor_argument)
|
||||
constructor_argument
|
||||
}
|
||||
let mut add_fields_branch = |variant, info_type, arguments, field_len| {
|
||||
let variant = Ident::new(variant, Span::call_site());
|
||||
let info_type = Ident::new(info_type, Span::call_site());
|
||||
variant_info.push(quote! {
|
||||
#bevy_reflect_path::VariantInfo::#variant(
|
||||
#bevy_reflect_path::#info_type::new(#arguments)
|
||||
)
|
||||
});
|
||||
enum_field_len.push(quote! {
|
||||
#unit{..} => #field_len
|
||||
});
|
||||
enum_variant_type.push(quote! {
|
||||
#unit{..} => #bevy_reflect_path::VariantType::#variant
|
||||
});
|
||||
};
|
||||
|
||||
let mut push_variant =
|
||||
|_variant: &EnumVariant, arguments: proc_macro2::TokenStream, field_len: usize| {
|
||||
#[cfg(feature = "documentation")]
|
||||
let with_docs = {
|
||||
let doc = quote::ToTokens::to_token_stream(&_variant.doc);
|
||||
Some(quote!(.with_docs(#doc)))
|
||||
};
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let with_docs: Option<proc_macro2::TokenStream> = None;
|
||||
|
||||
variant_info.push(quote! {
|
||||
#bevy_reflect_path::VariantInfo::#variant_type_ident(
|
||||
#bevy_reflect_path::#variant_info_ident::new(#arguments)
|
||||
#with_docs
|
||||
)
|
||||
});
|
||||
enum_field_len.push(quote! {
|
||||
#unit{..} => #field_len
|
||||
});
|
||||
enum_variant_type.push(quote! {
|
||||
#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident
|
||||
});
|
||||
};
|
||||
|
||||
match &variant.fields {
|
||||
EnumVariantFields::Unit => {
|
||||
add_fields_branch("Unit", "UnitVariantInfo", quote!(#name), 0usize);
|
||||
push_variant(variant, quote!(#name), 0);
|
||||
}
|
||||
EnumVariantFields::Unnamed(fields) => {
|
||||
let (field_len, argument) = for_fields(fields, |reflect_idx, declar, field| {
|
||||
let declar_field = syn::Index::from(declar);
|
||||
let args = get_field_args(fields, |reflect_idx, declaration_index, field| {
|
||||
let declar_field = syn::Index::from(declaration_index);
|
||||
enum_field_at.push(quote! {
|
||||
#unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value)
|
||||
});
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let with_docs = {
|
||||
let doc = quote::ToTokens::to_token_stream(&field.doc);
|
||||
Some(quote!(.with_docs(#doc)))
|
||||
};
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let with_docs: Option<proc_macro2::TokenStream> = None;
|
||||
|
||||
let field_ty = &field.data.ty;
|
||||
quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) }
|
||||
quote! {
|
||||
#bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx)
|
||||
#with_docs
|
||||
}
|
||||
});
|
||||
let arguments = quote!(#name, &[ #(#argument),* ]);
|
||||
add_fields_branch("Tuple", "TupleVariantInfo", arguments, field_len);
|
||||
|
||||
let field_len = args.len();
|
||||
push_variant(variant, quote!(#name, &[ #(#args),* ]), field_len);
|
||||
}
|
||||
EnumVariantFields::Named(fields) => {
|
||||
let (field_len, argument) = for_fields(fields, |reflect_idx, _, field| {
|
||||
let args = get_field_args(fields, |reflect_idx, _, field| {
|
||||
let field_ident = field.data.ident.as_ref().unwrap();
|
||||
let field_name = field_ident.to_string();
|
||||
enum_field.push(quote! {
|
||||
@ -357,11 +409,23 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
||||
#unit{ .. } if #ref_index == #reflect_idx => Some(#field_name)
|
||||
});
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let with_docs = {
|
||||
let doc = quote::ToTokens::to_token_stream(&field.doc);
|
||||
Some(quote!(.with_docs(#doc)))
|
||||
};
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let with_docs: Option<proc_macro2::TokenStream> = None;
|
||||
|
||||
let field_ty = &field.data.ty;
|
||||
quote! { #bevy_reflect_path::NamedField::new::<#field_ty>(#field_name) }
|
||||
quote! {
|
||||
#bevy_reflect_path::NamedField::new::<#field_ty>(#field_name)
|
||||
#with_docs
|
||||
}
|
||||
});
|
||||
let arguments = quote!(#name, &[ #(#argument),* ]);
|
||||
add_fields_branch("Struct", "StructVariantInfo", arguments, field_len);
|
||||
|
||||
let field_len = args.len();
|
||||
push_variant(variant, quote!(#name, &[ #(#args),* ]), field_len);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,15 +51,46 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let field_generator = {
|
||||
let docs = reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| quote::ToTokens::to_token_stream(&field.doc));
|
||||
quote! {
|
||||
#(#bevy_reflect_path::NamedField::new::<#field_types>(#field_names).with_docs(#docs) ,)*
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let field_generator = {
|
||||
quote! {
|
||||
#(#bevy_reflect_path::NamedField::new::<#field_types>(#field_names) ,)*
|
||||
}
|
||||
};
|
||||
|
||||
let string_name = struct_name.to_string();
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let info_generator = {
|
||||
let doc = reflect_struct.meta().doc();
|
||||
quote! {
|
||||
#bevy_reflect_path::StructInfo::new::<Self>(#string_name, &fields).with_docs(#doc)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let info_generator = {
|
||||
quote! {
|
||||
#bevy_reflect_path::StructInfo::new::<Self>(#string_name, &fields)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_impl = impl_typed(
|
||||
struct_name,
|
||||
reflect_struct.meta().generics(),
|
||||
quote! {
|
||||
let fields = [
|
||||
#(#bevy_reflect_path::NamedField::new::<#field_types>(#field_names),)*
|
||||
];
|
||||
let info = #bevy_reflect_path::StructInfo::new::<Self>(#string_name, &fields);
|
||||
let fields = [#field_generator];
|
||||
let info = #info_generator;
|
||||
#bevy_reflect_path::TypeInfo::Struct(info)
|
||||
},
|
||||
bevy_reflect_path,
|
||||
|
||||
@ -35,15 +35,46 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let field_generator = {
|
||||
let docs = reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| quote::ToTokens::to_token_stream(&field.doc));
|
||||
quote! {
|
||||
#(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents).with_docs(#docs) ,)*
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let field_generator = {
|
||||
quote! {
|
||||
#(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents) ,)*
|
||||
}
|
||||
};
|
||||
|
||||
let string_name = struct_name.to_string();
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let info_generator = {
|
||||
let doc = reflect_struct.meta().doc();
|
||||
quote! {
|
||||
#bevy_reflect_path::TupleStructInfo::new::<Self>(#string_name, &fields).with_docs(#doc)
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let info_generator = {
|
||||
quote! {
|
||||
#bevy_reflect_path::TupleStructInfo::new::<Self>(#string_name, &fields)
|
||||
}
|
||||
};
|
||||
|
||||
let typed_impl = impl_typed(
|
||||
struct_name,
|
||||
reflect_struct.meta().generics(),
|
||||
quote! {
|
||||
let fields = [
|
||||
#(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents),)*
|
||||
];
|
||||
let info = #bevy_reflect_path::TupleStructInfo::new::<Self>(#string_name, &fields);
|
||||
let fields = [#field_generator];
|
||||
let info = #info_generator;
|
||||
#bevy_reflect_path::TypeInfo::TupleStruct(info)
|
||||
},
|
||||
bevy_reflect_path,
|
||||
|
||||
@ -12,11 +12,19 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream {
|
||||
let partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path);
|
||||
let debug_fn = meta.traits().get_debug_impl();
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let with_docs = {
|
||||
let doc = quote::ToTokens::to_token_stream(meta.doc());
|
||||
Some(quote!(.with_docs(#doc)))
|
||||
};
|
||||
#[cfg(not(feature = "documentation"))]
|
||||
let with_docs: Option<proc_macro2::TokenStream> = None;
|
||||
|
||||
let typed_impl = impl_typed(
|
||||
type_name,
|
||||
meta.generics(),
|
||||
quote! {
|
||||
let info = #bevy_reflect_path::ValueInfo::new::<Self>();
|
||||
let info = #bevy_reflect_path::ValueInfo::new::<Self>() #with_docs;
|
||||
#bevy_reflect_path::TypeInfo::Value(info)
|
||||
},
|
||||
bevy_reflect_path,
|
||||
|
||||
@ -16,6 +16,8 @@ extern crate proc_macro;
|
||||
|
||||
mod container_attributes;
|
||||
mod derive_data;
|
||||
#[cfg(feature = "documentation")]
|
||||
mod documentation;
|
||||
mod enum_utility;
|
||||
mod field_attributes;
|
||||
mod from_reflect;
|
||||
@ -95,11 +97,16 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[proc_macro]
|
||||
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
||||
let def = parse_macro_input!(input as ReflectValueDef);
|
||||
impls::impl_value(&ReflectMeta::new(
|
||||
let meta = ReflectMeta::new(
|
||||
&def.type_name,
|
||||
&def.generics,
|
||||
def.traits.unwrap_or_default(),
|
||||
))
|
||||
);
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs));
|
||||
|
||||
impls::impl_value(&meta)
|
||||
}
|
||||
|
||||
/// A replacement for `#[derive(Reflect)]` to be used with foreign types which
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::container_attributes::ReflectTraits;
|
||||
use proc_macro2::Ident;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::token::{Paren, Where};
|
||||
use syn::{parenthesized, Generics};
|
||||
use syn::{parenthesized, Attribute, Generics};
|
||||
|
||||
/// A struct used to define a simple reflected value type (such as primitives).
|
||||
///
|
||||
@ -19,6 +19,8 @@ use syn::{parenthesized, Generics};
|
||||
/// foo<T1, T2> where T1: Bar (TraitA, TraitB)
|
||||
/// ```
|
||||
pub(crate) struct ReflectValueDef {
|
||||
#[allow(dead_code)]
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub type_name: Ident,
|
||||
pub generics: Generics,
|
||||
pub traits: Option<ReflectTraits>,
|
||||
@ -26,6 +28,7 @@ pub(crate) struct ReflectValueDef {
|
||||
|
||||
impl Parse for ReflectValueDef {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
let type_ident = input.parse::<Ident>()?;
|
||||
let generics = input.parse::<Generics>()?;
|
||||
let mut lookahead = input.lookahead1();
|
||||
@ -43,6 +46,7 @@ impl Parse for ReflectValueDef {
|
||||
}
|
||||
|
||||
Ok(ReflectValueDef {
|
||||
attrs,
|
||||
type_name: type_ident,
|
||||
generics: Generics {
|
||||
where_clause,
|
||||
|
||||
50
crates/bevy_reflect/examples/reflect_docs.rs
Normal file
50
crates/bevy_reflect/examples/reflect_docs.rs
Normal file
@ -0,0 +1,50 @@
|
||||
//! This example illustrates how you can reflect doc comments.
|
||||
//!
|
||||
//! There may be cases where you may want to collect a reflected item's documentation.
|
||||
//! For example, you may want to generate schemas or other external documentation for scripting.
|
||||
//! Or perhaps you want your custom editor to display tooltips for certain properties that match the documentation.
|
||||
//!
|
||||
//! These scenarios can readily be achieved by using `bevy_reflect` with the `documentation` feature.
|
||||
|
||||
use bevy_reflect::{Reflect, TypeInfo, Typed};
|
||||
|
||||
fn main() {
|
||||
//! This function will simply demonstrate how you can access a type's documentation.
|
||||
//!
|
||||
//! Please note that the code below uses a standard struct with named fields; however, this isn't
|
||||
//! exclusive to them. It can work for all kinds of data types including tuple structs and enums too!
|
||||
|
||||
/// The struct that defines our player.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let player = Player::new("Urist McPlayer");
|
||||
/// ```
|
||||
#[derive(Reflect)]
|
||||
struct Player {
|
||||
/// The player's name.
|
||||
name: String,
|
||||
/// The player's current health points.
|
||||
hp: u8,
|
||||
// Some regular comment (i.e. not a doc comment)
|
||||
max_hp: u8,
|
||||
}
|
||||
|
||||
// Using `TypeInfo` we can access all of the doc comments on the `Player` struct above:
|
||||
let player_info = <Player as Typed>::type_info();
|
||||
|
||||
// From here, we already have access to the struct's docs:
|
||||
let player_docs = player_info.docs().unwrap();
|
||||
assert_eq!(" The struct that defines our player.\n\n # Example\n\n ```\n let player = Player::new(\"Urist McPlayer\");\n ```", player_docs);
|
||||
println!("=====[ Player ]=====\n{}", player_docs);
|
||||
|
||||
// We can then iterate through our struct's fields to get their documentation as well:
|
||||
if let TypeInfo::Struct(struct_info) = player_info {
|
||||
for field in struct_info.iter() {
|
||||
let field_name = field.name();
|
||||
let field_docs = field.docs().unwrap_or("<NO_DOCUMENTATION>");
|
||||
println!("-----[ Player::{} ]-----\n{}", field_name, field_docs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,8 @@ pub struct ArrayInfo {
|
||||
item_type_name: &'static str,
|
||||
item_type_id: TypeId,
|
||||
capacity: usize,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl ArrayInfo {
|
||||
@ -65,9 +67,17 @@ impl ArrayInfo {
|
||||
item_type_name: std::any::type_name::<TItem>(),
|
||||
item_type_id: TypeId::of::<TItem>(),
|
||||
capacity,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this array.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The compile-time capacity of the array.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
@ -106,6 +116,12 @@ impl ArrayInfo {
|
||||
pub fn item_is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.item_type_id
|
||||
}
|
||||
|
||||
/// The docstring of this array, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// A fixed-size list of reflected values.
|
||||
|
||||
@ -137,6 +137,8 @@ pub struct EnumInfo {
|
||||
type_id: TypeId,
|
||||
variants: Box<[VariantInfo]>,
|
||||
variant_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl EnumInfo {
|
||||
@ -160,9 +162,17 @@ impl EnumInfo {
|
||||
type_id: TypeId::of::<TEnum>(),
|
||||
variants: variants.to_vec().into_boxed_slice(),
|
||||
variant_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this enum.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// Get a variant with the given name.
|
||||
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
|
||||
self.variant_indices
|
||||
@ -227,6 +237,12 @@ impl EnumInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this enum, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the fields in the current enum variant.
|
||||
|
||||
@ -72,6 +72,16 @@ impl VariantInfo {
|
||||
Self::Unit(info) => info.name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The docstring of the underlying variant, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Struct(info) => info.docs(),
|
||||
Self::Tuple(info) => info.docs(),
|
||||
Self::Unit(info) => info.docs(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type info for struct variants.
|
||||
@ -80,6 +90,8 @@ pub struct StructVariantInfo {
|
||||
name: &'static str,
|
||||
fields: Box<[NamedField]>,
|
||||
field_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl StructVariantInfo {
|
||||
@ -90,9 +102,17 @@ impl StructVariantInfo {
|
||||
name,
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
field_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this variant.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The name of this variant.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
@ -132,6 +152,12 @@ impl StructVariantInfo {
|
||||
.map(|(index, field)| (field.name(), index))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The docstring of this variant, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Type info for tuple variants.
|
||||
@ -139,6 +165,8 @@ impl StructVariantInfo {
|
||||
pub struct TupleVariantInfo {
|
||||
name: &'static str,
|
||||
fields: Box<[UnnamedField]>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl TupleVariantInfo {
|
||||
@ -147,9 +175,17 @@ impl TupleVariantInfo {
|
||||
Self {
|
||||
name,
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this variant.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The name of this variant.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
@ -169,22 +205,46 @@ impl TupleVariantInfo {
|
||||
pub fn field_len(&self) -> usize {
|
||||
self.fields.len()
|
||||
}
|
||||
|
||||
/// The docstring of this variant, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// Type info for unit variants.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UnitVariantInfo {
|
||||
name: &'static str,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl UnitVariantInfo {
|
||||
/// Create a new [`UnitVariantInfo`].
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self { name }
|
||||
Self {
|
||||
name,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this variant.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The name of this variant.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// The docstring of this variant, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ pub struct NamedField {
|
||||
name: &'static str,
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl NamedField {
|
||||
@ -16,9 +18,17 @@ impl NamedField {
|
||||
name,
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this field.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The name of the field.
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
@ -40,6 +50,12 @@ impl NamedField {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this field, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// The unnamed field of a reflected tuple or tuple struct.
|
||||
@ -48,6 +64,8 @@ pub struct UnnamedField {
|
||||
index: usize,
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl UnnamedField {
|
||||
@ -56,9 +74,17 @@ impl UnnamedField {
|
||||
index,
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this field.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// Returns the index of the field.
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
@ -80,4 +106,10 @@ impl UnnamedField {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this field, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
@ -835,6 +835,160 @@ mod tests {
|
||||
assert!(info.is::<MyDynamic>());
|
||||
}
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
mod docstrings {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_not_contain_docs() {
|
||||
// Regular comments do not count as doc comments,
|
||||
// and are therefore not reflected.
|
||||
#[derive(Reflect)]
|
||||
struct SomeStruct;
|
||||
|
||||
let info = <SomeStruct as Typed>::type_info();
|
||||
assert_eq!(None, info.docs());
|
||||
|
||||
/*
|
||||
* Block comments do not count as doc comments,
|
||||
* and are therefore not reflected.
|
||||
*/
|
||||
#[derive(Reflect)]
|
||||
struct SomeOtherStruct;
|
||||
|
||||
let info = <SomeOtherStruct as Typed>::type_info();
|
||||
assert_eq!(None, info.docs());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_contain_docs() {
|
||||
/// Some struct.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// let some_struct = SomeStruct;
|
||||
/// ```
|
||||
#[derive(Reflect)]
|
||||
struct SomeStruct;
|
||||
|
||||
let info = <SomeStruct as Typed>::type_info();
|
||||
assert_eq!(
|
||||
Some(" Some struct.\n\n # Example\n\n ```ignore\n let some_struct = SomeStruct;\n ```"),
|
||||
info.docs()
|
||||
);
|
||||
|
||||
#[doc = "The compiler automatically converts `///`-style comments into `#[doc]` attributes."]
|
||||
#[doc = "Of course, you _could_ use the attribute directly if you wanted to."]
|
||||
#[doc = "Both will be reflected."]
|
||||
#[derive(Reflect)]
|
||||
struct SomeOtherStruct;
|
||||
|
||||
let info = <SomeOtherStruct as Typed>::type_info();
|
||||
assert_eq!(
|
||||
Some("The compiler automatically converts `///`-style comments into `#[doc]` attributes.\nOf course, you _could_ use the attribute directly if you wanted to.\nBoth will be reflected."),
|
||||
info.docs()
|
||||
);
|
||||
|
||||
/// Some tuple struct.
|
||||
#[derive(Reflect)]
|
||||
struct SomeTupleStruct(usize);
|
||||
|
||||
let info = <SomeTupleStruct as Typed>::type_info();
|
||||
assert_eq!(Some(" Some tuple struct."), info.docs());
|
||||
|
||||
/// Some enum.
|
||||
#[derive(Reflect)]
|
||||
enum SomeEnum {
|
||||
Foo,
|
||||
}
|
||||
|
||||
let info = <SomeEnum as Typed>::type_info();
|
||||
assert_eq!(Some(" Some enum."), info.docs());
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SomePrimitive;
|
||||
impl_reflect_value!(
|
||||
/// Some primitive for which we have attributed custom documentation.
|
||||
SomePrimitive
|
||||
);
|
||||
|
||||
let info = <SomePrimitive as Typed>::type_info();
|
||||
assert_eq!(
|
||||
Some(" Some primitive for which we have attributed custom documentation."),
|
||||
info.docs()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fields_should_contain_docs() {
|
||||
#[derive(Reflect)]
|
||||
struct SomeStruct {
|
||||
/// The name
|
||||
name: String,
|
||||
/// The index
|
||||
index: usize,
|
||||
// Not documented...
|
||||
data: Vec<i32>,
|
||||
}
|
||||
|
||||
let info = <SomeStruct as Typed>::type_info();
|
||||
if let TypeInfo::Struct(info) = info {
|
||||
let mut fields = info.iter();
|
||||
assert_eq!(Some(" The name"), fields.next().unwrap().docs());
|
||||
assert_eq!(Some(" The index"), fields.next().unwrap().docs());
|
||||
assert_eq!(None, fields.next().unwrap().docs());
|
||||
} else {
|
||||
panic!("expected struct info");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variants_should_contain_docs() {
|
||||
#[derive(Reflect)]
|
||||
enum SomeEnum {
|
||||
// Not documented...
|
||||
Nothing,
|
||||
/// Option A
|
||||
A(
|
||||
/// Index
|
||||
usize,
|
||||
),
|
||||
/// Option B
|
||||
B {
|
||||
/// Name
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
let info = <SomeEnum as Typed>::type_info();
|
||||
if let TypeInfo::Enum(info) = info {
|
||||
let mut variants = info.iter();
|
||||
assert_eq!(None, variants.next().unwrap().docs());
|
||||
|
||||
let variant = variants.next().unwrap();
|
||||
assert_eq!(Some(" Option A"), variant.docs());
|
||||
if let VariantInfo::Tuple(variant) = variant {
|
||||
let field = variant.field_at(0).unwrap();
|
||||
assert_eq!(Some(" Index"), field.docs());
|
||||
} else {
|
||||
panic!("expected tuple variant")
|
||||
}
|
||||
|
||||
let variant = variants.next().unwrap();
|
||||
assert_eq!(Some(" Option B"), variant.docs());
|
||||
if let VariantInfo::Struct(variant) = variant {
|
||||
let field = variant.field_at(0).unwrap();
|
||||
assert_eq!(Some(" Name"), field.docs());
|
||||
} else {
|
||||
panic!("expected struct variant")
|
||||
}
|
||||
} else {
|
||||
panic!("expected enum info");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_reflect() {
|
||||
trait TestTrait: Reflect {}
|
||||
|
||||
@ -34,6 +34,8 @@ pub struct ListInfo {
|
||||
type_id: TypeId,
|
||||
item_type_name: &'static str,
|
||||
item_type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl ListInfo {
|
||||
@ -44,9 +46,17 @@ impl ListInfo {
|
||||
type_id: TypeId::of::<TList>(),
|
||||
item_type_name: std::any::type_name::<TItem>(),
|
||||
item_type_id: TypeId::of::<TItem>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this list.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The [type name] of the list.
|
||||
///
|
||||
/// [type name]: std::any::type_name
|
||||
@ -80,6 +90,12 @@ impl ListInfo {
|
||||
pub fn item_is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.item_type_id
|
||||
}
|
||||
|
||||
/// The docstring of this list, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of reflected values.
|
||||
|
||||
@ -68,6 +68,8 @@ pub struct MapInfo {
|
||||
key_type_id: TypeId,
|
||||
value_type_name: &'static str,
|
||||
value_type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl MapInfo {
|
||||
@ -80,9 +82,17 @@ impl MapInfo {
|
||||
key_type_id: TypeId::of::<TKey>(),
|
||||
value_type_name: std::any::type_name::<TValue>(),
|
||||
value_type_id: TypeId::of::<TValue>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this map.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The [type name] of the map.
|
||||
///
|
||||
/// [type name]: std::any::type_name
|
||||
@ -133,6 +143,12 @@ impl MapInfo {
|
||||
pub fn value_is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.value_type_id
|
||||
}
|
||||
|
||||
/// The docstring of this map, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
const HASH_ERROR: &str = "the given key does not support hashing";
|
||||
|
||||
@ -74,6 +74,8 @@ pub struct StructInfo {
|
||||
type_id: TypeId,
|
||||
fields: Box<[NamedField]>,
|
||||
field_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl StructInfo {
|
||||
@ -97,9 +99,17 @@ impl StructInfo {
|
||||
type_id: TypeId::of::<T>(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
field_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this struct.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// Get the field with the given name.
|
||||
pub fn field(&self, name: &str) -> Option<&NamedField> {
|
||||
self.field_indices
|
||||
@ -152,6 +162,12 @@ impl StructInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this struct, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the field values of a struct.
|
||||
|
||||
@ -134,6 +134,8 @@ pub struct TupleInfo {
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
fields: Box<[UnnamedField]>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl TupleInfo {
|
||||
@ -148,9 +150,17 @@ impl TupleInfo {
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this tuple.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// Get the field at the given index.
|
||||
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
||||
self.fields.get(index)
|
||||
@ -182,6 +192,12 @@ impl TupleInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this tuple, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// A tuple which allows fields to be added at runtime.
|
||||
|
||||
@ -53,6 +53,8 @@ pub struct TupleStructInfo {
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
fields: Box<[UnnamedField]>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl TupleStructInfo {
|
||||
@ -69,9 +71,17 @@ impl TupleStructInfo {
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this struct.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// Get the field at the given index.
|
||||
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
||||
self.fields.get(index)
|
||||
@ -112,6 +122,12 @@ impl TupleStructInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this struct, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the field values of a tuple struct.
|
||||
|
||||
@ -146,6 +146,22 @@ impl TypeInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id()
|
||||
}
|
||||
|
||||
/// The docstring of the underlying type, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Struct(info) => info.docs(),
|
||||
Self::TupleStruct(info) => info.docs(),
|
||||
Self::Tuple(info) => info.docs(),
|
||||
Self::List(info) => info.docs(),
|
||||
Self::Array(info) => info.docs(),
|
||||
Self::Map(info) => info.docs(),
|
||||
Self::Enum(info) => info.docs(),
|
||||
Self::Value(info) => info.docs(),
|
||||
Self::Dynamic(info) => info.docs(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A container for compile-time info related to general value types, including primitives.
|
||||
@ -160,6 +176,8 @@ impl TypeInfo {
|
||||
pub struct ValueInfo {
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl ValueInfo {
|
||||
@ -167,9 +185,17 @@ impl ValueInfo {
|
||||
Self {
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this value.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, doc: Option<&'static str>) -> Self {
|
||||
Self { docs: doc, ..self }
|
||||
}
|
||||
|
||||
/// The [type name] of the value.
|
||||
///
|
||||
/// [type name]: std::any::type_name
|
||||
@ -186,6 +212,12 @@ impl ValueInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this dynamic value, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// A container for compile-time info related to Bevy's _dynamic_ types, including primitives.
|
||||
@ -200,6 +232,8 @@ impl ValueInfo {
|
||||
pub struct DynamicInfo {
|
||||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl DynamicInfo {
|
||||
@ -207,9 +241,17 @@ impl DynamicInfo {
|
||||
Self {
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the docstring for this dynamic value.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
|
||||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// The [type name] of the dynamic value.
|
||||
///
|
||||
/// [type name]: std::any::type_name
|
||||
@ -226,4 +268,10 @@ impl DynamicInfo {
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id
|
||||
}
|
||||
|
||||
/// The docstring of this value, if any.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user