Adding Debug implementations for App, Stage, Schedule, Query, QueryState, etc. (#6214)

# Objective

- Adding Debug implementations for App, Stage, Schedule, Query, QueryState.
- Fixes #1130.

## Solution

- Implemented std::fmt::Debug for a number of structures.

---

## Changelog

Also added Debug implementations for ParallelSystemExecutor, SingleThreadedExecutor, various RunCriteria structures, SystemContainer, and SystemDescriptor.

Opinions are sure to differ as to what information to provide in a Debug implementation.  Best guess was taken for this initial version for these structures.


Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
This commit is contained in:
targrub 2022-10-10 20:59:38 +00:00
parent 55d126cab9
commit 9a597b758e
10 changed files with 202 additions and 6 deletions

View file

@ -70,12 +70,32 @@ pub struct App {
sub_apps: HashMap<AppLabelId, SubApp>, sub_apps: HashMap<AppLabelId, SubApp>,
} }
impl Debug for App {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "App {{ sub_apps: ")?;
f.debug_map()
.entries(self.sub_apps.iter().map(|(k, v)| (k, v)))
.finish()?;
write!(f, "}}")
}
}
/// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns. /// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns.
struct SubApp { struct SubApp {
app: App, app: App,
runner: Box<dyn Fn(&mut World, &mut App)>, runner: Box<dyn Fn(&mut World, &mut App)>,
} }
impl Debug for SubApp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SubApp {{ app: ")?;
f.debug_map()
.entries(self.app.sub_apps.iter().map(|(k, v)| (k, v)))
.finish()?;
write!(f, "}}")
}
}
impl Default for App { impl Default for App {
fn default() -> Self { fn default() -> Self {
let mut app = App::empty(); let mut app = App::empty();

View file

@ -35,6 +35,17 @@ pub struct QueryState<Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
pub(crate) filter_state: F::State, pub(crate) filter_state: F::State,
} }
impl<Q: WorldQuery, F: ReadOnlyWorldQuery> std::fmt::Debug for QueryState<Q, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"QueryState<Q, F> matched_table_ids: {} matched_archetype_ids: {}",
self.matched_table_ids.len(),
self.matched_archetype_ids.len()
)
}
}
impl<Q: WorldQuery, F: ReadOnlyWorldQuery> FromWorld for QueryState<Q, F> { impl<Q: WorldQuery, F: ReadOnlyWorldQuery> FromWorld for QueryState<Q, F> {
fn from_world(world: &mut World) -> Self { fn from_world(world: &mut World) -> Self {
world.query_filtered() world.query_filtered()

View file

@ -1,4 +1,5 @@
use crate::{schedule::SystemContainer, world::World}; use crate::{schedule::SystemContainer, world::World};
use core::fmt::Debug;
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
pub trait ParallelSystemExecutor: Downcast + Send + Sync { pub trait ParallelSystemExecutor: Downcast + Send + Sync {
@ -8,9 +9,15 @@ pub trait ParallelSystemExecutor: Downcast + Send + Sync {
fn run_systems(&mut self, systems: &mut [SystemContainer], world: &mut World); fn run_systems(&mut self, systems: &mut [SystemContainer], world: &mut World);
} }
impl Debug for dyn ParallelSystemExecutor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dyn ParallelSystemExecutor")
}
}
impl_downcast!(ParallelSystemExecutor); impl_downcast!(ParallelSystemExecutor);
#[derive(Default)] #[derive(Debug, Default)]
pub struct SingleThreadedExecutor; pub struct SingleThreadedExecutor;
impl ParallelSystemExecutor for SingleThreadedExecutor { impl ParallelSystemExecutor for SingleThreadedExecutor {

View file

@ -37,7 +37,7 @@ use bevy_utils::HashMap;
/// In this way, the properties of the child schedule can be set differently from the parent. /// In this way, the properties of the child schedule can be set differently from the parent.
/// For example, it can be set to run only once during app execution, while the parent schedule /// For example, it can be set to run only once during app execution, while the parent schedule
/// runs indefinitely. /// runs indefinitely.
#[derive(Default)] #[derive(Debug, Default)]
pub struct Schedule { pub struct Schedule {
stages: HashMap<StageLabelId, Box<dyn Stage>>, stages: HashMap<StageLabelId, Box<dyn Stage>>,
stage_order: Vec<StageLabelId>, stage_order: Vec<StageLabelId>,

View file

@ -1,8 +1,10 @@
use crate::{ use crate::{
prelude::System,
schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId}, schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId},
system::{BoxedSystem, IntoSystem, Local}, system::{BoxedSystem, IntoSystem, Local},
world::World, world::World,
}; };
use core::fmt::Debug;
use std::borrow::Cow; use std::borrow::Cow;
/// Determines whether a system should be executed or not, and how many times it should be ran each /// Determines whether a system should be executed or not, and how many times it should be ran each
@ -66,7 +68,7 @@ impl From<bool> for ShouldRun {
} }
} }
#[derive(Default)] #[derive(Debug, Default)]
pub(crate) struct BoxedRunCriteria { pub(crate) struct BoxedRunCriteria {
criteria_system: Option<BoxedSystem<(), ShouldRun>>, criteria_system: Option<BoxedSystem<(), ShouldRun>>,
initialized: bool, initialized: bool,
@ -93,6 +95,7 @@ impl BoxedRunCriteria {
} }
} }
#[derive(Debug)]
pub(crate) enum RunCriteriaInner { pub(crate) enum RunCriteriaInner {
Single(BoxedSystem<(), ShouldRun>), Single(BoxedSystem<(), ShouldRun>),
Piped { Piped {
@ -101,6 +104,7 @@ pub(crate) enum RunCriteriaInner {
}, },
} }
#[derive(Debug)]
pub(crate) struct RunCriteriaContainer { pub(crate) struct RunCriteriaContainer {
pub(crate) should_run: ShouldRun, pub(crate) should_run: ShouldRun,
pub(crate) inner: RunCriteriaInner, pub(crate) inner: RunCriteriaInner,
@ -165,17 +169,19 @@ impl GraphNode for RunCriteriaContainer {
} }
} }
#[derive(Debug)]
pub enum RunCriteriaDescriptorOrLabel { pub enum RunCriteriaDescriptorOrLabel {
Descriptor(RunCriteriaDescriptor), Descriptor(RunCriteriaDescriptor),
Label(RunCriteriaLabelId), Label(RunCriteriaLabelId),
} }
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) enum DuplicateLabelStrategy { pub(crate) enum DuplicateLabelStrategy {
Panic, Panic,
Discard, Discard,
} }
#[derive(Debug)]
pub struct RunCriteriaDescriptor { pub struct RunCriteriaDescriptor {
pub(crate) system: RunCriteriaSystem, pub(crate) system: RunCriteriaSystem,
pub(crate) label: Option<RunCriteriaLabelId>, pub(crate) label: Option<RunCriteriaLabelId>,
@ -184,6 +190,7 @@ pub struct RunCriteriaDescriptor {
pub(crate) after: Vec<RunCriteriaLabelId>, pub(crate) after: Vec<RunCriteriaLabelId>,
} }
#[derive(Debug)]
pub(crate) enum RunCriteriaSystem { pub(crate) enum RunCriteriaSystem {
Single(BoxedSystem<(), ShouldRun>), Single(BoxedSystem<(), ShouldRun>),
Piped(BoxedSystem<ShouldRun, ShouldRun>), Piped(BoxedSystem<ShouldRun, ShouldRun>),
@ -326,6 +333,7 @@ where
} }
} }
#[derive(Debug)]
pub struct RunCriteria { pub struct RunCriteria {
label: RunCriteriaLabelId, label: RunCriteriaLabelId,
} }
@ -346,3 +354,49 @@ impl RunCriteria {
} }
} }
} }
impl Debug for dyn System<In = (), Out = ShouldRun> + 'static {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"System {} with In=(), Out=ShouldRun: {{{}}}",
self.name(),
{
if self.is_send() {
if self.is_exclusive() {
"is_send is_exclusive"
} else {
"is_send"
}
} else if self.is_exclusive() {
"is_exclusive"
} else {
""
}
},
)
}
}
impl Debug for dyn System<In = ShouldRun, Out = ShouldRun> + 'static {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"System {} with In=ShouldRun, Out=ShouldRun: {{{}}}",
self.name(),
{
if self.is_send() {
if self.is_exclusive() {
"is_send is_exclusive"
} else {
"is_send"
}
} else if self.is_exclusive() {
"is_exclusive"
} else {
""
}
},
)
}
}

View file

@ -14,9 +14,10 @@ use crate::{
}; };
use bevy_ecs_macros::Resource; use bevy_ecs_macros::Resource;
use bevy_utils::{tracing::warn, HashMap, HashSet}; use bevy_utils::{tracing::warn, HashMap, HashSet};
use core::fmt::Debug;
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
use super::IntoSystemDescriptor; use super::{IntoSystemDescriptor, Schedule};
/// A type that can run as a step of a [`Schedule`](super::Schedule). /// A type that can run as a step of a [`Schedule`](super::Schedule).
pub trait Stage: Downcast + Send + Sync { pub trait Stage: Downcast + Send + Sync {
@ -25,6 +26,18 @@ pub trait Stage: Downcast + Send + Sync {
fn run(&mut self, world: &mut World); fn run(&mut self, world: &mut World);
} }
impl Debug for dyn Stage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(as_systemstage) = self.as_any().downcast_ref::<SystemStage>() {
write!(f, "{:?}", as_systemstage)
} else if let Some(as_schedule) = self.as_any().downcast_ref::<Schedule>() {
write!(f, "{:?}", as_schedule)
} else {
write!(f, "Unknown dyn Stage")
}
}
}
impl_downcast!(Stage); impl_downcast!(Stage);
/// When this resource is present in the `App`'s `Resources`, /// When this resource is present in the `App`'s `Resources`,
@ -584,6 +597,64 @@ impl SystemStage {
} }
Ok(labels) Ok(labels)
} }
pub fn vec_system_container_debug(
&self,
name: &str,
v: &Vec<SystemContainer>,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{}: ", name)?;
if v.len() > 1 {
writeln!(f, "[")?;
for sc in v.iter() {
writeln!(f, "{:?},", sc)?;
}
write!(f, "], ")
} else {
write!(f, "{:?}, ", v)
}
}
}
impl std::fmt::Debug for SystemStage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SystemStage: {{ ")?;
write!(
f,
"world_id: {:?}, executor: {:?}, stage_run_criteria: {:?}, run_criteria: {:?}, ",
self.world_id, self.executor, self.stage_run_criteria, self.run_criteria
)?;
self.vec_system_container_debug("exclusive_at_start", &self.exclusive_at_start, f)?;
self.vec_system_container_debug(
"exclusive_before_commands",
&self.exclusive_before_commands,
f,
)?;
self.vec_system_container_debug("exclusive_at_end", &self.exclusive_at_end, f)?;
self.vec_system_container_debug("parallel", &self.parallel, f)?;
write!(
f,
"systems_modified: {:?}, uninitialized_run_criteria: {:?}, ",
self.systems_modified, self.uninitialized_run_criteria
)?;
write!(
f,
"uninitialized_at_start: {:?}, uninitialized_before_commands: {:?}, ",
self.uninitialized_at_start, self.uninitialized_before_commands
)?;
write!(
f,
"uninitialized_at_end: {:?}, uninitialized_parallel: {:?}, ",
self.uninitialized_at_end, self.uninitialized_parallel
)?;
write!(
f,
"last_tick_check: {:?}, apply_buffers: {:?}, ",
self.last_tick_check, self.apply_buffers
)?;
write!(f, "must_read_resource: {:?}}}", self.must_read_resource)
}
} }
/// Sorts given system containers topologically, populates their resolved dependencies /// Sorts given system containers topologically, populates their resolved dependencies

View file

@ -6,6 +6,7 @@ use crate::{
}, },
system::System, system::System,
}; };
use core::fmt::Debug;
use std::borrow::Cow; use std::borrow::Cow;
pub struct SystemContainer { pub struct SystemContainer {
@ -83,6 +84,12 @@ impl SystemContainer {
} }
} }
impl Debug for SystemContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{{:?}}}", &self.system())
}
}
impl GraphNode for SystemContainer { impl GraphNode for SystemContainer {
type Label = SystemLabelId; type Label = SystemLabelId;

View file

@ -4,7 +4,7 @@ use crate::{
}; };
/// Configures ambiguity detection for a single system. /// Configures ambiguity detection for a single system.
#[derive(Default)] #[derive(Debug, Default)]
pub(crate) enum AmbiguityDetection { pub(crate) enum AmbiguityDetection {
#[default] #[default]
Check, Check,
@ -41,6 +41,7 @@ pub(crate) enum AmbiguityDetection {
/// .with_system(do_the_other_thing.after(Something)) /// .with_system(do_the_other_thing.after(Something))
/// .with_system(do_something_else.at_end()); /// .with_system(do_something_else.at_end());
/// ``` /// ```
#[derive(Debug)]
pub struct SystemDescriptor { pub struct SystemDescriptor {
pub(crate) system: BoxedSystem<(), ()>, pub(crate) system: BoxedSystem<(), ()>,
pub(crate) exclusive_insertion_point: Option<ExclusiveInsertionPoint>, pub(crate) exclusive_insertion_point: Option<ExclusiveInsertionPoint>,

View file

@ -280,6 +280,12 @@ pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
pub(crate) change_tick: u32, pub(crate) change_tick: u32,
} }
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> std::fmt::Debug for Query<'w, 's, Q, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Query {{ matched entities: {}, world: {:?}, state: {:?}, last_change_tick: {}, change_tick: {} }}", self.iter().count(), self.world, self.state, self.last_change_tick, self.change_tick)
}
}
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
/// Creates a new query. /// Creates a new query.
/// ///

View file

@ -1,4 +1,5 @@
use bevy_utils::tracing::warn; use bevy_utils::tracing::warn;
use core::fmt::Debug;
use crate::{ use crate::{
archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId, archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId,
@ -95,3 +96,21 @@ pub(crate) fn check_system_change_tick(
*last_change_tick = change_tick.wrapping_sub(MAX_CHANGE_AGE); *last_change_tick = change_tick.wrapping_sub(MAX_CHANGE_AGE);
} }
} }
impl Debug for dyn System<In = (), Out = ()> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "System {}: {{{}}}", self.name(), {
if self.is_send() {
if self.is_exclusive() {
"is_send is_exclusive"
} else {
"is_send"
}
} else if self.is_exclusive() {
"is_exclusive"
} else {
""
}
},)
}
}