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>,
}
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.
struct SubApp {
app: 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 {
fn default() -> Self {
let mut app = App::empty();

View file

@ -35,6 +35,17 @@ pub struct QueryState<Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
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> {
fn from_world(world: &mut World) -> Self {
world.query_filtered()

View file

@ -1,4 +1,5 @@
use crate::{schedule::SystemContainer, world::World};
use core::fmt::Debug;
use downcast_rs::{impl_downcast, Downcast};
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);
}
impl Debug for dyn ParallelSystemExecutor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "dyn ParallelSystemExecutor")
}
}
impl_downcast!(ParallelSystemExecutor);
#[derive(Default)]
#[derive(Debug, Default)]
pub struct 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.
/// For example, it can be set to run only once during app execution, while the parent schedule
/// runs indefinitely.
#[derive(Default)]
#[derive(Debug, Default)]
pub struct Schedule {
stages: HashMap<StageLabelId, Box<dyn Stage>>,
stage_order: Vec<StageLabelId>,

View file

@ -1,8 +1,10 @@
use crate::{
prelude::System,
schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId},
system::{BoxedSystem, IntoSystem, Local},
world::World,
};
use core::fmt::Debug;
use std::borrow::Cow;
/// 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 {
criteria_system: Option<BoxedSystem<(), ShouldRun>>,
initialized: bool,
@ -93,6 +95,7 @@ impl BoxedRunCriteria {
}
}
#[derive(Debug)]
pub(crate) enum RunCriteriaInner {
Single(BoxedSystem<(), ShouldRun>),
Piped {
@ -101,6 +104,7 @@ pub(crate) enum RunCriteriaInner {
},
}
#[derive(Debug)]
pub(crate) struct RunCriteriaContainer {
pub(crate) should_run: ShouldRun,
pub(crate) inner: RunCriteriaInner,
@ -165,17 +169,19 @@ impl GraphNode for RunCriteriaContainer {
}
}
#[derive(Debug)]
pub enum RunCriteriaDescriptorOrLabel {
Descriptor(RunCriteriaDescriptor),
Label(RunCriteriaLabelId),
}
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum DuplicateLabelStrategy {
Panic,
Discard,
}
#[derive(Debug)]
pub struct RunCriteriaDescriptor {
pub(crate) system: RunCriteriaSystem,
pub(crate) label: Option<RunCriteriaLabelId>,
@ -184,6 +190,7 @@ pub struct RunCriteriaDescriptor {
pub(crate) after: Vec<RunCriteriaLabelId>,
}
#[derive(Debug)]
pub(crate) enum RunCriteriaSystem {
Single(BoxedSystem<(), ShouldRun>),
Piped(BoxedSystem<ShouldRun, ShouldRun>),
@ -326,6 +333,7 @@ where
}
}
#[derive(Debug)]
pub struct RunCriteria {
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_utils::{tracing::warn, HashMap, HashSet};
use core::fmt::Debug;
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).
pub trait Stage: Downcast + Send + Sync {
@ -25,6 +26,18 @@ pub trait Stage: Downcast + Send + Sync {
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);
/// When this resource is present in the `App`'s `Resources`,
@ -584,6 +597,64 @@ impl SystemStage {
}
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

View file

@ -6,6 +6,7 @@ use crate::{
},
system::System,
};
use core::fmt::Debug;
use std::borrow::Cow;
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 {
type Label = SystemLabelId;

View file

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

View file

@ -1,4 +1,5 @@
use bevy_utils::tracing::warn;
use core::fmt::Debug;
use crate::{
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);
}
}
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 {
""
}
},)
}
}