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"
|
readme = "README.md"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = []
|
||||||
|
# Provides Bevy-related reflection implementations
|
||||||
bevy = ["glam", "smallvec", "bevy_math"]
|
bevy = ["glam", "smallvec", "bevy_math"]
|
||||||
|
# When enabled, allows documentation comments to be accessed via reflection
|
||||||
|
documentation = ["bevy_reflect_derive/documentation"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# bevy
|
# bevy
|
||||||
@ -31,3 +35,8 @@ glam = { version = "0.21", features = ["serde"], optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ron = "0.8.0"
|
ron = "0.8.0"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "reflect_docs"
|
||||||
|
path = "examples/reflect_docs.rs"
|
||||||
|
required-features = ["documentation"]
|
||||||
|
|||||||
@ -11,6 +11,11 @@ keywords = ["bevy"]
|
|||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
# When enabled, allows documentation comments to be processed by the reflection macros
|
||||||
|
documentation = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0-dev" }
|
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.9.0-dev" }
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,9 @@ pub(crate) struct ReflectMeta<'a> {
|
|||||||
generics: &'a Generics,
|
generics: &'a Generics,
|
||||||
/// A cached instance of the path to the `bevy_reflect` crate.
|
/// A cached instance of the path to the `bevy_reflect` crate.
|
||||||
bevy_reflect_path: Path,
|
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`.
|
/// Struct data used by derive macros for `Reflect` and `FromReflect`.
|
||||||
@ -86,6 +89,9 @@ pub(crate) struct StructField<'a> {
|
|||||||
pub attrs: ReflectFieldAttr,
|
pub attrs: ReflectFieldAttr,
|
||||||
/// The index of this field within the struct.
|
/// The index of this field within the struct.
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
|
/// The documentation for this field, if any
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
pub doc: crate::documentation::Documentation,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a variant on an enum.
|
/// Represents a variant on an enum.
|
||||||
@ -100,6 +106,9 @@ pub(crate) struct EnumVariant<'a> {
|
|||||||
/// The index of this variant within the enum.
|
/// The index of this variant within the enum.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
|
/// The documentation for this variant, if any
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
pub doc: crate::documentation::Documentation,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum EnumVariantFields<'a> {
|
pub(crate) enum EnumVariantFields<'a> {
|
||||||
@ -123,6 +132,9 @@ impl<'a> ReflectDerive<'a> {
|
|||||||
// Should indicate whether `#[reflect_value]` was used
|
// Should indicate whether `#[reflect_value]` was used
|
||||||
let mut reflect_mode = None;
|
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()) {
|
for attribute in input.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
|
||||||
match attribute {
|
match attribute {
|
||||||
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
|
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);
|
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,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
||||||
|
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
let meta = meta.with_docs(doc);
|
||||||
|
|
||||||
// Use normal reflection if unspecified
|
// Use normal reflection if unspecified
|
||||||
let reflect_mode = reflect_mode.unwrap_or(ReflectMode::Normal);
|
let reflect_mode = reflect_mode.unwrap_or(ReflectMode::Normal);
|
||||||
|
|
||||||
if reflect_mode == ReflectMode::Value {
|
if reflect_mode == ReflectMode::Value {
|
||||||
return Ok(Self::Value(ReflectMeta::new(
|
return Ok(Self::Value(meta));
|
||||||
&input.ident,
|
|
||||||
&input.generics,
|
|
||||||
traits,
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return match &input.data {
|
return match &input.data {
|
||||||
Data::Struct(data) => {
|
Data::Struct(data) => {
|
||||||
let fields = Self::collect_struct_fields(&data.fields)?;
|
let fields = Self::collect_struct_fields(&data.fields)?;
|
||||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
|
||||||
let reflect_struct = ReflectStruct {
|
let reflect_struct = ReflectStruct {
|
||||||
meta,
|
meta,
|
||||||
serialization_denylist: members_to_serialization_denylist(
|
serialization_denylist: members_to_serialization_denylist(
|
||||||
@ -194,7 +212,6 @@ impl<'a> ReflectDerive<'a> {
|
|||||||
}
|
}
|
||||||
Data::Enum(data) => {
|
Data::Enum(data) => {
|
||||||
let variants = Self::collect_enum_variants(&data.variants)?;
|
let variants = Self::collect_enum_variants(&data.variants)?;
|
||||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
|
||||||
|
|
||||||
let reflect_enum = ReflectEnum { meta, variants };
|
let reflect_enum = ReflectEnum { meta, variants };
|
||||||
Ok(Self::Enum(reflect_enum))
|
Ok(Self::Enum(reflect_enum))
|
||||||
@ -216,6 +233,8 @@ impl<'a> ReflectDerive<'a> {
|
|||||||
index,
|
index,
|
||||||
attrs,
|
attrs,
|
||||||
data: field,
|
data: field,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
doc: crate::documentation::Documentation::from_attributes(&field.attrs),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.fold(
|
.fold(
|
||||||
@ -245,6 +264,8 @@ impl<'a> ReflectDerive<'a> {
|
|||||||
attrs: parse_field_attrs(&variant.attrs)?,
|
attrs: parse_field_attrs(&variant.attrs)?,
|
||||||
data: variant,
|
data: variant,
|
||||||
index,
|
index,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.fold(
|
.fold(
|
||||||
@ -263,9 +284,17 @@ impl<'a> ReflectMeta<'a> {
|
|||||||
type_name,
|
type_name,
|
||||||
generics,
|
generics,
|
||||||
bevy_reflect_path: utility::get_bevy_reflect_path(),
|
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.
|
/// The registered reflect traits on this struct.
|
||||||
pub fn traits(&self) -> &ReflectTraits {
|
pub fn traits(&self) -> &ReflectTraits {
|
||||||
&self.traits
|
&self.traits
|
||||||
@ -296,6 +325,12 @@ impl<'a> ReflectMeta<'a> {
|
|||||||
None,
|
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> {
|
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::enum_utility::{get_variant_constructors, EnumVariantConstructors};
|
||||||
use crate::impls::impl_typed;
|
use crate::impls::impl_typed;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Ident, Span};
|
use proc_macro2::{Ident, Span};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::Fields;
|
||||||
|
|
||||||
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream {
|
||||||
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
|
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();
|
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(
|
let typed_impl = impl_typed(
|
||||||
enum_name,
|
enum_name,
|
||||||
reflect_enum.meta().generics(),
|
reflect_enum.meta().generics(),
|
||||||
quote! {
|
quote! {
|
||||||
let variants = [#(#variant_info),*];
|
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::TypeInfo::Enum(info)
|
||||||
},
|
},
|
||||||
bevy_reflect_path,
|
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 name = ident.to_string();
|
||||||
let unit = reflect_enum.get_unit(ident);
|
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! {
|
enum_variant_name.push(quote! {
|
||||||
#unit{..} => #name
|
#unit{..} => #name
|
||||||
});
|
});
|
||||||
@ -293,10 +322,10 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
|||||||
#unit{..} => #variant_index
|
#unit{..} => #variant_index
|
||||||
});
|
});
|
||||||
|
|
||||||
fn for_fields(
|
fn get_field_args(
|
||||||
fields: &[StructField],
|
fields: &[StructField],
|
||||||
mut generate_for_field: impl FnMut(usize, usize, &StructField) -> proc_macro2::TokenStream,
|
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 constructor_argument = Vec::new();
|
||||||
let mut reflect_idx = 0;
|
let mut reflect_idx = 0;
|
||||||
for field in fields.iter() {
|
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));
|
constructor_argument.push(generate_for_field(reflect_idx, field.index, field));
|
||||||
reflect_idx += 1;
|
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 mut push_variant =
|
||||||
let info_type = Ident::new(info_type, Span::call_site());
|
|_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! {
|
variant_info.push(quote! {
|
||||||
#bevy_reflect_path::VariantInfo::#variant(
|
#bevy_reflect_path::VariantInfo::#variant_type_ident(
|
||||||
#bevy_reflect_path::#info_type::new(#arguments)
|
#bevy_reflect_path::#variant_info_ident::new(#arguments)
|
||||||
|
#with_docs
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
enum_field_len.push(quote! {
|
enum_field_len.push(quote! {
|
||||||
#unit{..} => #field_len
|
#unit{..} => #field_len
|
||||||
});
|
});
|
||||||
enum_variant_type.push(quote! {
|
enum_variant_type.push(quote! {
|
||||||
#unit{..} => #bevy_reflect_path::VariantType::#variant
|
#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
match &variant.fields {
|
match &variant.fields {
|
||||||
EnumVariantFields::Unit => {
|
EnumVariantFields::Unit => {
|
||||||
add_fields_branch("Unit", "UnitVariantInfo", quote!(#name), 0usize);
|
push_variant(variant, quote!(#name), 0);
|
||||||
}
|
}
|
||||||
EnumVariantFields::Unnamed(fields) => {
|
EnumVariantFields::Unnamed(fields) => {
|
||||||
let (field_len, argument) = for_fields(fields, |reflect_idx, declar, field| {
|
let args = get_field_args(fields, |reflect_idx, declaration_index, field| {
|
||||||
let declar_field = syn::Index::from(declar);
|
let declar_field = syn::Index::from(declaration_index);
|
||||||
enum_field_at.push(quote! {
|
enum_field_at.push(quote! {
|
||||||
#unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value)
|
#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;
|
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) => {
|
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_ident = field.data.ident.as_ref().unwrap();
|
||||||
let field_name = field_ident.to_string();
|
let field_name = field_ident.to_string();
|
||||||
enum_field.push(quote! {
|
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)
|
#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;
|
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();
|
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(
|
let typed_impl = impl_typed(
|
||||||
struct_name,
|
struct_name,
|
||||||
reflect_struct.meta().generics(),
|
reflect_struct.meta().generics(),
|
||||||
quote! {
|
quote! {
|
||||||
let fields = [
|
let fields = [#field_generator];
|
||||||
#(#bevy_reflect_path::NamedField::new::<#field_types>(#field_names),)*
|
let info = #info_generator;
|
||||||
];
|
|
||||||
let info = #bevy_reflect_path::StructInfo::new::<Self>(#string_name, &fields);
|
|
||||||
#bevy_reflect_path::TypeInfo::Struct(info)
|
#bevy_reflect_path::TypeInfo::Struct(info)
|
||||||
},
|
},
|
||||||
bevy_reflect_path,
|
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();
|
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(
|
let typed_impl = impl_typed(
|
||||||
struct_name,
|
struct_name,
|
||||||
reflect_struct.meta().generics(),
|
reflect_struct.meta().generics(),
|
||||||
quote! {
|
quote! {
|
||||||
let fields = [
|
let fields = [#field_generator];
|
||||||
#(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents),)*
|
let info = #info_generator;
|
||||||
];
|
|
||||||
let info = #bevy_reflect_path::TupleStructInfo::new::<Self>(#string_name, &fields);
|
|
||||||
#bevy_reflect_path::TypeInfo::TupleStruct(info)
|
#bevy_reflect_path::TypeInfo::TupleStruct(info)
|
||||||
},
|
},
|
||||||
bevy_reflect_path,
|
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 partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path);
|
||||||
let debug_fn = meta.traits().get_debug_impl();
|
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(
|
let typed_impl = impl_typed(
|
||||||
type_name,
|
type_name,
|
||||||
meta.generics(),
|
meta.generics(),
|
||||||
quote! {
|
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::TypeInfo::Value(info)
|
||||||
},
|
},
|
||||||
bevy_reflect_path,
|
bevy_reflect_path,
|
||||||
|
|||||||
@ -16,6 +16,8 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
mod container_attributes;
|
mod container_attributes;
|
||||||
mod derive_data;
|
mod derive_data;
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
mod documentation;
|
||||||
mod enum_utility;
|
mod enum_utility;
|
||||||
mod field_attributes;
|
mod field_attributes;
|
||||||
mod from_reflect;
|
mod from_reflect;
|
||||||
@ -95,11 +97,16 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
||||||
let def = parse_macro_input!(input as ReflectValueDef);
|
let def = parse_macro_input!(input as ReflectValueDef);
|
||||||
impls::impl_value(&ReflectMeta::new(
|
let meta = ReflectMeta::new(
|
||||||
&def.type_name,
|
&def.type_name,
|
||||||
&def.generics,
|
&def.generics,
|
||||||
def.traits.unwrap_or_default(),
|
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
|
/// 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 proc_macro2::Ident;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::token::{Paren, Where};
|
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).
|
/// 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)
|
/// foo<T1, T2> where T1: Bar (TraitA, TraitB)
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) struct ReflectValueDef {
|
pub(crate) struct ReflectValueDef {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub attrs: Vec<Attribute>,
|
||||||
pub type_name: Ident,
|
pub type_name: Ident,
|
||||||
pub generics: Generics,
|
pub generics: Generics,
|
||||||
pub traits: Option<ReflectTraits>,
|
pub traits: Option<ReflectTraits>,
|
||||||
@ -26,6 +28,7 @@ pub(crate) struct ReflectValueDef {
|
|||||||
|
|
||||||
impl Parse for ReflectValueDef {
|
impl Parse for ReflectValueDef {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let attrs = input.call(Attribute::parse_outer)?;
|
||||||
let type_ident = input.parse::<Ident>()?;
|
let type_ident = input.parse::<Ident>()?;
|
||||||
let generics = input.parse::<Generics>()?;
|
let generics = input.parse::<Generics>()?;
|
||||||
let mut lookahead = input.lookahead1();
|
let mut lookahead = input.lookahead1();
|
||||||
@ -43,6 +46,7 @@ impl Parse for ReflectValueDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(ReflectValueDef {
|
Ok(ReflectValueDef {
|
||||||
|
attrs,
|
||||||
type_name: type_ident,
|
type_name: type_ident,
|
||||||
generics: Generics {
|
generics: Generics {
|
||||||
where_clause,
|
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_name: &'static str,
|
||||||
item_type_id: TypeId,
|
item_type_id: TypeId,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArrayInfo {
|
impl ArrayInfo {
|
||||||
@ -65,9 +67,17 @@ impl ArrayInfo {
|
|||||||
item_type_name: std::any::type_name::<TItem>(),
|
item_type_name: std::any::type_name::<TItem>(),
|
||||||
item_type_id: TypeId::of::<TItem>(),
|
item_type_id: TypeId::of::<TItem>(),
|
||||||
capacity,
|
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.
|
/// The compile-time capacity of the array.
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.capacity
|
self.capacity
|
||||||
@ -106,6 +116,12 @@ impl ArrayInfo {
|
|||||||
pub fn item_is<T: Any>(&self) -> bool {
|
pub fn item_is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.item_type_id
|
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.
|
/// A fixed-size list of reflected values.
|
||||||
|
|||||||
@ -137,6 +137,8 @@ pub struct EnumInfo {
|
|||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
variants: Box<[VariantInfo]>,
|
variants: Box<[VariantInfo]>,
|
||||||
variant_indices: HashMap<&'static str, usize>,
|
variant_indices: HashMap<&'static str, usize>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnumInfo {
|
impl EnumInfo {
|
||||||
@ -160,9 +162,17 @@ impl EnumInfo {
|
|||||||
type_id: TypeId::of::<TEnum>(),
|
type_id: TypeId::of::<TEnum>(),
|
||||||
variants: variants.to_vec().into_boxed_slice(),
|
variants: variants.to_vec().into_boxed_slice(),
|
||||||
variant_indices,
|
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.
|
/// Get a variant with the given name.
|
||||||
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
|
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
|
||||||
self.variant_indices
|
self.variant_indices
|
||||||
@ -227,6 +237,12 @@ impl EnumInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// An iterator over the fields in the current enum variant.
|
||||||
|
|||||||
@ -72,6 +72,16 @@ impl VariantInfo {
|
|||||||
Self::Unit(info) => info.name(),
|
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.
|
/// Type info for struct variants.
|
||||||
@ -80,6 +90,8 @@ pub struct StructVariantInfo {
|
|||||||
name: &'static str,
|
name: &'static str,
|
||||||
fields: Box<[NamedField]>,
|
fields: Box<[NamedField]>,
|
||||||
field_indices: HashMap<&'static str, usize>,
|
field_indices: HashMap<&'static str, usize>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructVariantInfo {
|
impl StructVariantInfo {
|
||||||
@ -90,9 +102,17 @@ impl StructVariantInfo {
|
|||||||
name,
|
name,
|
||||||
fields: fields.to_vec().into_boxed_slice(),
|
fields: fields.to_vec().into_boxed_slice(),
|
||||||
field_indices,
|
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.
|
/// The name of this variant.
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
self.name
|
self.name
|
||||||
@ -132,6 +152,12 @@ impl StructVariantInfo {
|
|||||||
.map(|(index, field)| (field.name(), index))
|
.map(|(index, field)| (field.name(), index))
|
||||||
.collect()
|
.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.
|
/// Type info for tuple variants.
|
||||||
@ -139,6 +165,8 @@ impl StructVariantInfo {
|
|||||||
pub struct TupleVariantInfo {
|
pub struct TupleVariantInfo {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
fields: Box<[UnnamedField]>,
|
fields: Box<[UnnamedField]>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TupleVariantInfo {
|
impl TupleVariantInfo {
|
||||||
@ -147,9 +175,17 @@ impl TupleVariantInfo {
|
|||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
fields: fields.to_vec().into_boxed_slice(),
|
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.
|
/// The name of this variant.
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
self.name
|
self.name
|
||||||
@ -169,22 +205,46 @@ impl TupleVariantInfo {
|
|||||||
pub fn field_len(&self) -> usize {
|
pub fn field_len(&self) -> usize {
|
||||||
self.fields.len()
|
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.
|
/// Type info for unit variants.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UnitVariantInfo {
|
pub struct UnitVariantInfo {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnitVariantInfo {
|
impl UnitVariantInfo {
|
||||||
/// Create a new [`UnitVariantInfo`].
|
/// Create a new [`UnitVariantInfo`].
|
||||||
pub fn new(name: &'static str) -> Self {
|
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.
|
/// The name of this variant.
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
self.name
|
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,
|
name: &'static str,
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NamedField {
|
impl NamedField {
|
||||||
@ -16,9 +18,17 @@ impl NamedField {
|
|||||||
name,
|
name,
|
||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<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.
|
/// The name of the field.
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
self.name
|
self.name
|
||||||
@ -40,6 +50,12 @@ impl NamedField {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// The unnamed field of a reflected tuple or tuple struct.
|
||||||
@ -48,6 +64,8 @@ pub struct UnnamedField {
|
|||||||
index: usize,
|
index: usize,
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnnamedField {
|
impl UnnamedField {
|
||||||
@ -56,9 +74,17 @@ impl UnnamedField {
|
|||||||
index,
|
index,
|
||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<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.
|
/// Returns the index of the field.
|
||||||
pub fn index(&self) -> usize {
|
pub fn index(&self) -> usize {
|
||||||
self.index
|
self.index
|
||||||
@ -80,4 +106,10 @@ impl UnnamedField {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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>());
|
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]
|
#[test]
|
||||||
fn as_reflect() {
|
fn as_reflect() {
|
||||||
trait TestTrait: Reflect {}
|
trait TestTrait: Reflect {}
|
||||||
|
|||||||
@ -34,6 +34,8 @@ pub struct ListInfo {
|
|||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
item_type_name: &'static str,
|
item_type_name: &'static str,
|
||||||
item_type_id: TypeId,
|
item_type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListInfo {
|
impl ListInfo {
|
||||||
@ -44,9 +46,17 @@ impl ListInfo {
|
|||||||
type_id: TypeId::of::<TList>(),
|
type_id: TypeId::of::<TList>(),
|
||||||
item_type_name: std::any::type_name::<TItem>(),
|
item_type_name: std::any::type_name::<TItem>(),
|
||||||
item_type_id: TypeId::of::<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.
|
/// The [type name] of the list.
|
||||||
///
|
///
|
||||||
/// [type name]: std::any::type_name
|
/// [type name]: std::any::type_name
|
||||||
@ -80,6 +90,12 @@ impl ListInfo {
|
|||||||
pub fn item_is<T: Any>(&self) -> bool {
|
pub fn item_is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.item_type_id
|
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.
|
/// A list of reflected values.
|
||||||
|
|||||||
@ -68,6 +68,8 @@ pub struct MapInfo {
|
|||||||
key_type_id: TypeId,
|
key_type_id: TypeId,
|
||||||
value_type_name: &'static str,
|
value_type_name: &'static str,
|
||||||
value_type_id: TypeId,
|
value_type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MapInfo {
|
impl MapInfo {
|
||||||
@ -80,9 +82,17 @@ impl MapInfo {
|
|||||||
key_type_id: TypeId::of::<TKey>(),
|
key_type_id: TypeId::of::<TKey>(),
|
||||||
value_type_name: std::any::type_name::<TValue>(),
|
value_type_name: std::any::type_name::<TValue>(),
|
||||||
value_type_id: TypeId::of::<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.
|
/// The [type name] of the map.
|
||||||
///
|
///
|
||||||
/// [type name]: std::any::type_name
|
/// [type name]: std::any::type_name
|
||||||
@ -133,6 +143,12 @@ impl MapInfo {
|
|||||||
pub fn value_is<T: Any>(&self) -> bool {
|
pub fn value_is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.value_type_id
|
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";
|
const HASH_ERROR: &str = "the given key does not support hashing";
|
||||||
|
|||||||
@ -74,6 +74,8 @@ pub struct StructInfo {
|
|||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
fields: Box<[NamedField]>,
|
fields: Box<[NamedField]>,
|
||||||
field_indices: HashMap<&'static str, usize>,
|
field_indices: HashMap<&'static str, usize>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructInfo {
|
impl StructInfo {
|
||||||
@ -97,9 +99,17 @@ impl StructInfo {
|
|||||||
type_id: TypeId::of::<T>(),
|
type_id: TypeId::of::<T>(),
|
||||||
fields: fields.to_vec().into_boxed_slice(),
|
fields: fields.to_vec().into_boxed_slice(),
|
||||||
field_indices,
|
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.
|
/// Get the field with the given name.
|
||||||
pub fn field(&self, name: &str) -> Option<&NamedField> {
|
pub fn field(&self, name: &str) -> Option<&NamedField> {
|
||||||
self.field_indices
|
self.field_indices
|
||||||
@ -152,6 +162,12 @@ impl StructInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// An iterator over the field values of a struct.
|
||||||
|
|||||||
@ -134,6 +134,8 @@ pub struct TupleInfo {
|
|||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
fields: Box<[UnnamedField]>,
|
fields: Box<[UnnamedField]>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TupleInfo {
|
impl TupleInfo {
|
||||||
@ -148,9 +150,17 @@ impl TupleInfo {
|
|||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<T>(),
|
type_id: TypeId::of::<T>(),
|
||||||
fields: fields.to_vec().into_boxed_slice(),
|
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.
|
/// Get the field at the given index.
|
||||||
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
||||||
self.fields.get(index)
|
self.fields.get(index)
|
||||||
@ -182,6 +192,12 @@ impl TupleInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// A tuple which allows fields to be added at runtime.
|
||||||
|
|||||||
@ -53,6 +53,8 @@ pub struct TupleStructInfo {
|
|||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
fields: Box<[UnnamedField]>,
|
fields: Box<[UnnamedField]>,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TupleStructInfo {
|
impl TupleStructInfo {
|
||||||
@ -69,9 +71,17 @@ impl TupleStructInfo {
|
|||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<T>(),
|
type_id: TypeId::of::<T>(),
|
||||||
fields: fields.to_vec().into_boxed_slice(),
|
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.
|
/// Get the field at the given index.
|
||||||
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {
|
||||||
self.fields.get(index)
|
self.fields.get(index)
|
||||||
@ -112,6 +122,12 @@ impl TupleStructInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// An iterator over the field values of a tuple struct.
|
||||||
|
|||||||
@ -146,6 +146,22 @@ impl TypeInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id()
|
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.
|
/// A container for compile-time info related to general value types, including primitives.
|
||||||
@ -160,6 +176,8 @@ impl TypeInfo {
|
|||||||
pub struct ValueInfo {
|
pub struct ValueInfo {
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueInfo {
|
impl ValueInfo {
|
||||||
@ -167,9 +185,17 @@ impl ValueInfo {
|
|||||||
Self {
|
Self {
|
||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<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.
|
/// The [type name] of the value.
|
||||||
///
|
///
|
||||||
/// [type name]: std::any::type_name
|
/// [type name]: std::any::type_name
|
||||||
@ -186,6 +212,12 @@ impl ValueInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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.
|
/// A container for compile-time info related to Bevy's _dynamic_ types, including primitives.
|
||||||
@ -200,6 +232,8 @@ impl ValueInfo {
|
|||||||
pub struct DynamicInfo {
|
pub struct DynamicInfo {
|
||||||
type_name: &'static str,
|
type_name: &'static str,
|
||||||
type_id: TypeId,
|
type_id: TypeId,
|
||||||
|
#[cfg(feature = "documentation")]
|
||||||
|
docs: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicInfo {
|
impl DynamicInfo {
|
||||||
@ -207,9 +241,17 @@ impl DynamicInfo {
|
|||||||
Self {
|
Self {
|
||||||
type_name: std::any::type_name::<T>(),
|
type_name: std::any::type_name::<T>(),
|
||||||
type_id: TypeId::of::<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.
|
/// The [type name] of the dynamic value.
|
||||||
///
|
///
|
||||||
/// [type name]: std::any::type_name
|
/// [type name]: std::any::type_name
|
||||||
@ -226,4 +268,10 @@ impl DynamicInfo {
|
|||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
TypeId::of::<T>() == self.type_id
|
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