mirror of
https://github.com/bevyengine/bevy
synced 2025-01-02 08:18:59 +00:00
Added PrettyPrintFunctionInfo
Used to help reduce code duplication for overloaded functions and to give users the option to pretty-print FunctionInfo
This commit is contained in:
parent
c2a18d593f
commit
e5f2085ddf
4 changed files with 195 additions and 43 deletions
|
@ -487,30 +487,17 @@ impl_type_path!((in bevy_reflect) DynamicFunction<'env>);
|
||||||
/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
/// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||||
///
|
///
|
||||||
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
||||||
|
///
|
||||||
|
/// If the function is [overloaded], the output will include the signatures of all overloads as a set.
|
||||||
|
/// For example, `DynamicFunction(fn add{(_: i32, _: i32) -> i32, (_: f32, _: f32) -> f32})`.
|
||||||
|
///
|
||||||
|
/// [overloaded]: DynamicFunction::with_overload
|
||||||
impl<'env> Debug for DynamicFunction<'env> {
|
impl<'env> Debug for DynamicFunction<'env> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
||||||
write!(f, "DynamicFunction(fn {name}(")?;
|
write!(f, "DynamicFunction(fn {name}")?;
|
||||||
|
self.function_map.debug(f)?;
|
||||||
match self.info() {
|
write!(f, ")")
|
||||||
FunctionInfoType::Standard(info) => {
|
|
||||||
for (index, arg) in info.args().iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = arg.name().unwrap_or("_");
|
|
||||||
let ty = arg.type_path();
|
|
||||||
write!(f, "{name}: {ty}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = info.return_info().type_path();
|
|
||||||
write!(f, ") -> {ret})")
|
|
||||||
}
|
|
||||||
FunctionInfoType::Overloaded(_) => {
|
|
||||||
todo!("overloaded functions are not yet debuggable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,4 +812,39 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_debug_dynamic_function() {
|
||||||
|
fn greet(name: &String) -> String {
|
||||||
|
format!("Hello, {}!", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
let function = greet.into_function();
|
||||||
|
let debug = format!("{:?}", function);
|
||||||
|
assert_eq!(debug, "DynamicFunction(fn bevy_reflect::func::dynamic_function::tests::should_debug_dynamic_function::greet(_: &alloc::string::String) -> alloc::string::String)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_debug_anonymous_dynamic_function() {
|
||||||
|
let function = (|a: i32, b: i32| a + b).into_function();
|
||||||
|
let debug = format!("{:?}", function);
|
||||||
|
assert_eq!(debug, "DynamicFunction(fn _(_: i32, _: i32) -> i32)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_debug_overloaded_dynamic_function() {
|
||||||
|
fn add<T: Add<Output = T>>(a: T, b: T) -> T {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = add::<i32>
|
||||||
|
.into_function()
|
||||||
|
.with_overload(add::<f32>)
|
||||||
|
.with_name("add");
|
||||||
|
let debug = format!("{:?}", func);
|
||||||
|
assert_eq!(
|
||||||
|
debug,
|
||||||
|
"DynamicFunction(fn add{(_: i32, _: i32) -> i32, (_: f32, _: f32) -> f32})"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,30 +382,17 @@ impl<'env> DynamicFunctionMut<'env> {
|
||||||
/// This takes the format: `DynamicFunctionMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
/// This takes the format: `DynamicFunctionMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`.
|
||||||
///
|
///
|
||||||
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
/// Names for arguments and the function itself are optional and will default to `_` if not provided.
|
||||||
|
///
|
||||||
|
/// If the function is [overloaded], the output will include the signatures of all overloads as a set.
|
||||||
|
/// For example, `DynamicFunctionMut(fn add{(_: i32, _: i32) -> i32, (_: f32, _: f32) -> f32})`.
|
||||||
|
///
|
||||||
|
/// [overloaded]: DynamicFunctionMut::with_overload
|
||||||
impl<'env> Debug for DynamicFunctionMut<'env> {
|
impl<'env> Debug for DynamicFunctionMut<'env> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
||||||
write!(f, "DynamicFunctionMut(fn {name}(")?;
|
write!(f, "DynamicFunctionMut(fn {name}")?;
|
||||||
|
self.function_map.debug(f)?;
|
||||||
match self.info() {
|
write!(f, ")")
|
||||||
FunctionInfoType::Standard(info) => {
|
|
||||||
for (index, arg) in info.args().iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = arg.name().unwrap_or("_");
|
|
||||||
let ty = arg.type_path();
|
|
||||||
write!(f, "{name}: {ty}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = info.return_info().type_path();
|
|
||||||
write!(f, ") -> {ret})")
|
|
||||||
}
|
|
||||||
FunctionInfoType::Overloaded(_) => {
|
|
||||||
todo!("overloaded functions are not yet debuggable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use crate::func::signature::ArgumentSignature;
|
use crate::func::signature::ArgumentSignature;
|
||||||
use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionInfoType, FunctionOverloadError};
|
use crate::func::{
|
||||||
|
ArgList, FunctionError, FunctionInfo, FunctionInfoType, FunctionOverloadError,
|
||||||
|
PrettyPrintFunctionInfo,
|
||||||
|
};
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use bevy_utils::hashbrown::HashMap;
|
use bevy_utils::hashbrown::HashMap;
|
||||||
|
use core::fmt::{Debug, Formatter};
|
||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
|
|
||||||
/// A helper type for storing a mapping of overloaded functions
|
/// A helper type for storing a mapping of overloaded functions
|
||||||
|
@ -216,6 +220,21 @@ impl<F> FunctionMap<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
// `(arg0: i32, arg1: i32) -> ()`
|
||||||
|
Self::Single(_, info) => PrettyPrintFunctionInfo::new(info).fmt(f),
|
||||||
|
// `{(arg0: i32, arg1: i32) -> (), (arg0: f32, arg1: f32) -> ()}`
|
||||||
|
Self::Overloaded(_, infos, _) => {
|
||||||
|
let mut set = f.debug_set();
|
||||||
|
for info in infos.iter() {
|
||||||
|
set.entry(&PrettyPrintFunctionInfo::new(info));
|
||||||
|
}
|
||||||
|
set.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,6 +3,7 @@ use alloc::{borrow::Cow, vec};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::{boxed::Box, format, vec};
|
use alloc::{boxed::Box, format, vec};
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Formatter};
|
||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
use variadics_please::all_tuples;
|
use variadics_please::all_tuples;
|
||||||
|
|
||||||
|
@ -211,6 +212,28 @@ impl FunctionInfo {
|
||||||
pub fn return_info(&self) -> &ReturnInfo {
|
pub fn return_info(&self) -> &ReturnInfo {
|
||||||
&self.return_info
|
&self.return_info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a wrapper around this info that implements [`Debug`] for pretty-printing the function.
|
||||||
|
///
|
||||||
|
/// This can be useful for more readable debugging and logging.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_reflect::func::{FunctionInfo, TypedFunction};
|
||||||
|
/// #
|
||||||
|
/// fn add(a: i32, b: i32) -> i32 {
|
||||||
|
/// a + b
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let info = add.get_function_info();
|
||||||
|
///
|
||||||
|
/// let pretty = info.pretty_printer();
|
||||||
|
/// assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
|
||||||
|
/// ```
|
||||||
|
pub fn pretty_printer(&self) -> PrettyPrintFunctionInfo {
|
||||||
|
PrettyPrintFunctionInfo::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
/// Information about the return type of a [`DynamicFunction`] or [`DynamicFunctionMut`].
|
||||||
|
@ -240,6 +263,86 @@ impl ReturnInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper around [`FunctionInfo`] that implements [`Debug`] for pretty-printing function information.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bevy_reflect::func::{FunctionInfo, PrettyPrintFunctionInfo, TypedFunction};
|
||||||
|
/// #
|
||||||
|
/// fn add(a: i32, b: i32) -> i32 {
|
||||||
|
/// a + b
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let info = add.get_function_info();
|
||||||
|
///
|
||||||
|
/// let pretty = PrettyPrintFunctionInfo::new(&info);
|
||||||
|
/// assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
|
||||||
|
/// ```
|
||||||
|
pub struct PrettyPrintFunctionInfo<'a> {
|
||||||
|
info: &'a FunctionInfo,
|
||||||
|
include_fn_token: bool,
|
||||||
|
include_name: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PrettyPrintFunctionInfo<'a> {
|
||||||
|
/// Create a new pretty-printer for the given [`FunctionInfo`].
|
||||||
|
pub fn new(info: &'a FunctionInfo) -> Self {
|
||||||
|
Self {
|
||||||
|
info,
|
||||||
|
include_fn_token: false,
|
||||||
|
include_name: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include the function name in the pretty-printed output.
|
||||||
|
pub fn include_name(mut self) -> Self {
|
||||||
|
self.include_name = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include the `fn` token in the pretty-printed output.
|
||||||
|
pub fn include_fn_token(mut self) -> Self {
|
||||||
|
self.include_fn_token = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Debug for PrettyPrintFunctionInfo<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
if self.include_fn_token {
|
||||||
|
write!(f, "fn")?;
|
||||||
|
|
||||||
|
if self.include_name {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self.include_name, self.info.name()) {
|
||||||
|
(true, Some(name)) => write!(f, "{}", name)?,
|
||||||
|
(true, None) => write!(f, "_")?,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "(")?;
|
||||||
|
|
||||||
|
// We manually write the args instead of using `DebugTuple` to avoid trailing commas
|
||||||
|
// and (when used with `{:#?}`) unnecessary newlines
|
||||||
|
for (index, arg) in self.info.args().iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = arg.name().unwrap_or("_");
|
||||||
|
let ty = arg.type_path();
|
||||||
|
write!(f, "{name}: {ty}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = self.info.return_info().type_path();
|
||||||
|
write!(f, ") -> {ret}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A static accessor to compile-time type information for functions.
|
/// A static accessor to compile-time type information for functions.
|
||||||
///
|
///
|
||||||
/// This is the equivalent of [`Typed`], but for function.
|
/// This is the equivalent of [`Typed`], but for function.
|
||||||
|
@ -520,4 +623,25 @@ mod tests {
|
||||||
assert_eq!(info.args()[1].type_path(), "i32");
|
assert_eq!(info.args()[1].type_path(), "i32");
|
||||||
assert_eq!(info.return_info().type_path(), "()");
|
assert_eq!(info.return_info().type_path(), "()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_pretty_print_info() {
|
||||||
|
fn add(a: i32, b: i32) -> i32 {
|
||||||
|
a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
let info = add.get_function_info().with_name("add");
|
||||||
|
|
||||||
|
let pretty = info.pretty_printer();
|
||||||
|
assert_eq!(format!("{:?}", pretty), "(_: i32, _: i32) -> i32");
|
||||||
|
|
||||||
|
let pretty = info.pretty_printer().include_fn_token();
|
||||||
|
assert_eq!(format!("{:?}", pretty), "fn(_: i32, _: i32) -> i32");
|
||||||
|
|
||||||
|
let pretty = info.pretty_printer().include_name();
|
||||||
|
assert_eq!(format!("{:?}", pretty), "add(_: i32, _: i32) -> i32");
|
||||||
|
|
||||||
|
let pretty = info.pretty_printer().include_fn_token().include_name();
|
||||||
|
assert_eq!(format!("{:?}", pretty), "fn add(_: i32, _: i32) -> i32");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue