Derive Error for more error types (#10240)

# Objective

Align all error-like types to implement `Error`.

Fixes  #10176

## Solution

- Derive `Error` on more types
- Refactor instances of manual implementations that could be derived

This adds thiserror as a dependency to bevy_transform, which might
increase compilation time -- but I don't know of any situation where you
might only use that but not any other crate that pulls in bevy_utils.

The `contributors` example has a `LoadContributorsError` type, but as
it's an example I have not updated it. Doing that would mean either
having a `use bevy_internal::utils::thiserror::Error;` in an example
file, or adding `thiserror` as a dev-dependency to the main `bevy`
crate.

---

## Changelog

- All `…Error` types now implement the `Error` trait
This commit is contained in:
Pascal Hertleif 2023-10-29 00:20:37 +02:00 committed by GitHub
parent 9d088dd144
commit 0c2c52a0cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 50 additions and 72 deletions

View File

@ -8,7 +8,7 @@ use bevy_ecs::{
ScheduleBuildSettings, ScheduleLabel, ScheduleBuildSettings, ScheduleLabel,
}, },
}; };
use bevy_utils::{intern::Interned, tracing::debug, HashMap, HashSet}; use bevy_utils::{intern::Interned, thiserror::Error, tracing::debug, HashMap, HashSet};
use std::{ use std::{
fmt::Debug, fmt::Debug,
panic::{catch_unwind, resume_unwind, AssertUnwindSafe}, panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
@ -28,7 +28,9 @@ pub use bevy_utils::label::DynEq;
/// A shorthand for `Interned<dyn AppLabel>`. /// A shorthand for `Interned<dyn AppLabel>`.
pub type InternedAppLabel = Interned<dyn AppLabel>; pub type InternedAppLabel = Interned<dyn AppLabel>;
#[derive(Debug, Error)]
pub(crate) enum AppError { pub(crate) enum AppError {
#[error("duplicate plugin {plugin_name:?}")]
DuplicatePlugin { plugin_name: String }, DuplicatePlugin { plugin_name: String },
} }

View File

@ -1,41 +1,28 @@
use std::fmt; use thiserror::Error;
use crate::entity::Entity; use crate::entity::Entity;
/// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState). /// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState).
// TODO: return the type_name as part of this error // TODO: return the type_name as part of this error
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Error)]
pub enum QueryEntityError { pub enum QueryEntityError {
/// The given [`Entity`]'s components do not match the query. /// The given [`Entity`]'s components do not match the query.
/// ///
/// Either it does not have a requested component, or it has a component which the query filters out. /// Either it does not have a requested component, or it has a component which the query filters out.
#[error("The components of entity {0:?} do not match the query")]
QueryDoesNotMatch(Entity), QueryDoesNotMatch(Entity),
/// The given [`Entity`] does not exist. /// The given [`Entity`] does not exist.
#[error("The entity {0:?} does not exist")]
NoSuchEntity(Entity), NoSuchEntity(Entity),
/// The [`Entity`] was requested mutably more than once. /// The [`Entity`] was requested mutably more than once.
/// ///
/// See [`QueryState::get_many_mut`](crate::query::QueryState::get_many_mut) for an example. /// See [`QueryState::get_many_mut`](crate::query::QueryState::get_many_mut) for an example.
#[error("The entity {0:?} was requested mutably more than once")]
AliasedMutability(Entity), AliasedMutability(Entity),
} }
impl std::error::Error for QueryEntityError {}
impl fmt::Display for QueryEntityError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
QueryEntityError::QueryDoesNotMatch(_) => {
write!(f, "The given entity's components do not match the query.")
}
QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."),
QueryEntityError::AliasedMutability(_) => {
write!(f, "The entity was requested mutably more than once.")
}
}
}
}
/// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`](crate::system::Query). /// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`](crate::system::Query).
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Error)]
pub enum QueryComponentError { pub enum QueryComponentError {
/// The [`Query`](crate::system::Query) does not have read access to the requested component. /// The [`Query`](crate::system::Query) does not have read access to the requested component.
/// ///
@ -66,6 +53,7 @@ pub enum QueryComponentError {
/// } /// }
/// # bevy_ecs::system::assert_is_system(get_missing_read_access_error); /// # bevy_ecs::system::assert_is_system(get_missing_read_access_error);
/// ``` /// ```
#[error("This query does not have read access to the requested component")]
MissingReadAccess, MissingReadAccess,
/// The [`Query`](crate::system::Query) does not have write access to the requested component. /// The [`Query`](crate::system::Query) does not have write access to the requested component.
/// ///
@ -93,59 +81,24 @@ pub enum QueryComponentError {
/// } /// }
/// # bevy_ecs::system::assert_is_system(get_missing_write_access_error); /// # bevy_ecs::system::assert_is_system(get_missing_write_access_error);
/// ``` /// ```
#[error("This query does not have write access to the requested component")]
MissingWriteAccess, MissingWriteAccess,
/// The given [`Entity`] does not have the requested component. /// The given [`Entity`] does not have the requested component.
#[error("The given entity does not have the requested component")]
MissingComponent, MissingComponent,
/// The requested [`Entity`] does not exist. /// The requested [`Entity`] does not exist.
#[error("The requested entity does not exist")]
NoSuchEntity, NoSuchEntity,
} }
impl std::error::Error for QueryComponentError {}
impl std::fmt::Display for QueryComponentError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
QueryComponentError::MissingReadAccess => {
write!(
f,
"This query does not have read access to the requested component."
)
}
QueryComponentError::MissingWriteAccess => {
write!(
f,
"This query does not have write access to the requested component."
)
}
QueryComponentError::MissingComponent => {
write!(f, "The given entity does not have the requested component.")
}
QueryComponentError::NoSuchEntity => {
write!(f, "The requested entity does not exist.")
}
}
}
}
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via /// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut). /// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
#[derive(Debug)] #[derive(Debug, Error)]
pub enum QuerySingleError { pub enum QuerySingleError {
/// No entity fits the query. /// No entity fits the query.
#[error("No entities fit the query {0}")]
NoEntities(&'static str), NoEntities(&'static str),
/// Multiple entities fit the query. /// Multiple entities fit the query.
#[error("Multiple entities fit the query {0}")]
MultipleEntities(&'static str), MultipleEntities(&'static str),
} }
impl std::error::Error for QuerySingleError {}
impl std::fmt::Display for QuerySingleError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
QuerySingleError::NoEntities(query) => write!(f, "No entities fit the query {query}"),
QuerySingleError::MultipleEntities(query) => {
write!(f, "Multiple entities fit the query {query}!")
}
}
}
}

View File

@ -3,6 +3,7 @@ use crate::system::{BoxedSystem, Command, IntoSystem};
use crate::world::World; use crate::world::World;
use crate::{self as bevy_ecs}; use crate::{self as bevy_ecs};
use bevy_ecs_macros::Component; use bevy_ecs_macros::Component;
use thiserror::Error;
/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized. /// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
#[derive(Component)] #[derive(Component)]
@ -197,15 +198,18 @@ impl Command for RunSystem {
} }
/// An operation with stored systems failed. /// An operation with stored systems failed.
#[derive(Debug)] #[derive(Debug, Error)]
pub enum RegisteredSystemError { pub enum RegisteredSystemError {
/// A system was run by id, but no system with that id was found. /// A system was run by id, but no system with that id was found.
/// ///
/// Did you forget to register it? /// Did you forget to register it?
#[error("System {0:?} was not registered")]
SystemIdNotRegistered(SystemId), SystemIdNotRegistered(SystemId),
/// A system tried to run itself recursively. /// A system tried to run itself recursively.
#[error("System {0:?} tried to run itself recursively")]
Recursive(SystemId), Recursive(SystemId),
/// A system tried to remove itself. /// A system tried to remove itself.
#[error("System {0:?} tried to remove itself")]
SelfRemove(SystemId), SelfRemove(SystemId),
} }

View File

@ -6,10 +6,12 @@ use bevy_ecs::{
schedule::SystemConfigs, schedule::SystemConfigs,
system::{StaticSystemParam, SystemParam, SystemParamItem}, system::{StaticSystemParam, SystemParam, SystemParamItem},
}; };
use bevy_utils::{HashMap, HashSet}; use bevy_utils::{thiserror::Error, HashMap, HashSet};
use std::marker::PhantomData; use std::marker::PhantomData;
#[derive(Debug, Error)]
pub enum PrepareAssetError<E: Send + Sync + 'static> { pub enum PrepareAssetError<E: Send + Sync + 'static> {
#[error("Failed to prepare asset")]
RetryNextUpdate(E), RetryNextUpdate(E),
} }

View File

@ -7,6 +7,7 @@ use crate::{
texture::FallbackImage, texture::FallbackImage,
}; };
pub use bevy_render_macros::AsBindGroup; pub use bevy_render_macros::AsBindGroup;
use bevy_utils::thiserror::Error;
use encase::ShaderType; use encase::ShaderType;
use std::ops::Deref; use std::ops::Deref;
use wgpu::{BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource}; use wgpu::{BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource};
@ -325,8 +326,10 @@ pub trait AsBindGroup {
} }
/// An error that occurs during [`AsBindGroup::as_bind_group`] calls. /// An error that occurs during [`AsBindGroup::as_bind_group`] calls.
#[derive(Debug, Error)]
pub enum AsBindGroupError { pub enum AsBindGroupError {
/// The bind group could not be generated. Try again next frame. /// The bind group could not be generated. Try again next frame.
#[error("The bind group could not be generated")]
RetryNextUpdate, RetryNextUpdate,
} }

View File

@ -16,6 +16,7 @@ bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.12.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" } bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] } bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] }
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
thiserror = "1.0"
[dev-dependencies] [dev-dependencies]
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }

View File

@ -6,6 +6,7 @@ use bevy_ecs::{
system::{Query, SystemParam}, system::{Query, SystemParam},
}; };
use bevy_hierarchy::{HierarchyQueryExt, Parent}; use bevy_hierarchy::{HierarchyQueryExt, Parent};
use thiserror::Error;
use crate::components::{GlobalTransform, Transform}; use crate::components::{GlobalTransform, Transform};
@ -63,14 +64,17 @@ fn map_error(err: QueryEntityError, ancestor: bool) -> ComputeGlobalTransformErr
} }
/// Error returned by [`TransformHelper::compute_global_transform`]. /// Error returned by [`TransformHelper::compute_global_transform`].
#[derive(Debug)] #[derive(Debug, Error)]
pub enum ComputeGlobalTransformError { pub enum ComputeGlobalTransformError {
/// The entity or one of its ancestors is missing the [`Transform`] component. /// The entity or one of its ancestors is missing the [`Transform`] component.
#[error("The entity {0:?} or one of its ancestors is missing the `Transform` component")]
MissingTransform(Entity), MissingTransform(Entity),
/// The entity does not exist. /// The entity does not exist.
#[error("The entity {0:?} does not exist")]
NoSuchEntity(Entity), NoSuchEntity(Entity),
/// An ancestor is missing. /// An ancestor is missing.
/// This probably means that your hierarchy has been improperly maintained. /// This probably means that your hierarchy has been improperly maintained.
#[error("The ancestor {0:?} is missing")]
MalformedHierarchy(Entity), MalformedHierarchy(Entity),
} }

View File

@ -19,6 +19,7 @@ use bevy_utils::{default, HashMap};
use bevy_window::{PrimaryWindow, Window, WindowResolution, WindowScaleFactorChanged}; use bevy_window::{PrimaryWindow, Window, WindowResolution, WindowScaleFactorChanged};
use std::fmt; use std::fmt;
use taffy::Taffy; use taffy::Taffy;
use thiserror::Error;
pub struct LayoutContext { pub struct LayoutContext {
pub scale_factor: f64, pub scale_factor: f64,
@ -228,10 +229,12 @@ with UI components as a child of an entity without UI components, results may be
} }
} }
#[derive(Debug)] #[derive(Debug, Error)]
pub enum LayoutError { pub enum LayoutError {
#[error("Invalid hierarchy")]
InvalidHierarchy, InvalidHierarchy,
TaffyError(taffy::error::TaffyError), #[error("Taffy error: {0}")]
TaffyError(#[from] taffy::error::TaffyError),
} }
/// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes. /// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes.

View File

@ -1,6 +1,9 @@
//! This example displays each contributor to the bevy source code as a bouncing bevy-ball. //! This example displays each contributor to the bevy source code as a bouncing bevy-ball.
use bevy::{prelude::*, utils::HashSet}; use bevy::{
prelude::*,
utils::{thiserror, HashSet},
};
use rand::{prelude::SliceRandom, Rng}; use rand::{prelude::SliceRandom, Rng};
use std::{ use std::{
env::VarError, env::VarError,
@ -309,9 +312,13 @@ fn move_system(time: Res<Time>, mut query: Query<(&Velocity, &mut Transform)>) {
} }
} }
#[derive(Debug, thiserror::Error)]
enum LoadContributorsError { enum LoadContributorsError {
IO(io::Error), #[error("An IO error occurred while reading the git log.")]
Var(VarError), Io(#[from] io::Error),
#[error("The CARGO_MANIFEST_DIR environment variable was not set.")]
Var(#[from] VarError),
#[error("The git process did not return a stdout handle.")]
Stdout, Stdout,
} }
@ -321,14 +328,13 @@ enum LoadContributorsError {
/// This function only works if `git` is installed and /// This function only works if `git` is installed and
/// the program is run through `cargo`. /// the program is run through `cargo`.
fn contributors() -> Result<Contributors, LoadContributorsError> { fn contributors() -> Result<Contributors, LoadContributorsError> {
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(LoadContributorsError::Var)?; let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;
let mut cmd = std::process::Command::new("git") let mut cmd = std::process::Command::new("git")
.args(["--no-pager", "log", "--pretty=format:%an"]) .args(["--no-pager", "log", "--pretty=format:%an"])
.current_dir(manifest_dir) .current_dir(manifest_dir)
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()?;
.map_err(LoadContributorsError::IO)?;
let stdout = cmd.stdout.take().ok_or(LoadContributorsError::Stdout)?; let stdout = cmd.stdout.take().ok_or(LoadContributorsError::Stdout)?;