mirror of
https://github.com/bevyengine/bevy
synced 2024-12-30 06:53:13 +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})`.
|
||||
///
|
||||
/// 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> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicFunction(fn {name}(")?;
|
||||
|
||||
match self.info() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
write!(f, "DynamicFunction(fn {name}")?;
|
||||
self.function_map.debug(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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})`.
|
||||
///
|
||||
/// 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> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let name = self.name().unwrap_or(&Cow::Borrowed("_"));
|
||||
write!(f, "DynamicFunctionMut(fn {name}(")?;
|
||||
|
||||
match self.info() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
write!(f, "DynamicFunctionMut(fn {name}")?;
|
||||
self.function_map.debug(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
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 bevy_utils::hashbrown::HashMap;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
/// 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)]
|
||||
|
|
|
@ -3,6 +3,7 @@ use alloc::{borrow::Cow, vec};
|
|||
#[cfg(not(feature = "std"))]
|
||||
use alloc::{boxed::Box, format, vec};
|
||||
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::ops::RangeInclusive;
|
||||
use variadics_please::all_tuples;
|
||||
|
||||
|
@ -211,6 +212,28 @@ impl FunctionInfo {
|
|||
pub fn return_info(&self) -> &ReturnInfo {
|
||||
&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`].
|
||||
|
@ -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.
|
||||
///
|
||||
/// 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.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