#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use bevy_ecs::prelude::*;
pub use anyhow;
pub use bevy_ui_mod_alerts::AlertsPlugin;
pub type ResultVec<T, E> = std::result::Result<T, Vec<E>>;
pub trait AnyhowAlertExt<In, Err, Marker>
where
Err: std::fmt::Display + Send + Sync + 'static,
{
fn anyhow_alert(self) -> impl System<In = In, Out = ()>;
}
impl<F, In, Err, Marker> AnyhowAlertExt<In, Err, Marker> for F
where
F: IntoSystem<In, Result<(), Err>, Marker>,
Err: std::fmt::Debug + std::fmt::Display + Send + Sync + 'static,
Marker: Send + Sync + 'static,
{
fn anyhow_alert(self) -> impl System<In = In, Out = ()> {
self.pipe(anyhow_alert_system)
}
}
pub fn anyhow_alert_system<Err>(In(input): In<Result<(), Err>>, commands: Commands)
where
Err: std::fmt::Display,
{
if let Err(error) = input {
AlertsPlugin::alert(In(vec![format!("{error}")]), commands)
}
}
pub trait AnyhowAlertsExt<In, Err, Marker>
where
Err: std::fmt::Debug + Send + Sync + 'static,
{
fn anyhow_alerts(self) -> impl System<In = In, Out = ()>;
}
impl<F, In, Err, Marker> AnyhowAlertsExt<In, Vec<Err>, Marker> for F
where
F: IntoSystem<In, Result<(), Vec<Err>>, Marker>,
Err: std::error::Error + Send + Sync + 'static,
Marker: Send + Sync + 'static,
{
fn anyhow_alerts(self) -> impl System<In = In, Out = ()> {
self.pipe(anyhow_alerts_system)
}
}
pub fn anyhow_alerts_system<Err>(In(input): In<Result<(), Vec<Err>>>, commands: Commands)
where
Err: std::fmt::Display,
{
if let Err(errors) = input {
let messages = errors
.into_iter()
.map(|error| format!("{error}"))
.collect::<Vec<_>>();
AlertsPlugin::alert(In(messages), commands)
}
}
#[cfg(test)]
mod tests {
use super::*;
use bevy::prelude::*;
use bevy_ui_mod_alerts::Alert;
use thiserror::Error;
#[derive(Debug, Error)]
#[error("testing!")]
struct TestError;
fn alternate_output(mut counter: Local<usize>) -> Result<(), TestError> {
*counter += 1;
if *counter % 2 == 1 {
Ok(())
} else {
Err(TestError)
}
}
fn alternate_output_many_errors(mut counter: Local<usize>) -> ResultVec<(), TestError> {
*counter += 1;
if *counter % 2 == 1 {
Ok(())
} else {
Err(vec![TestError])
}
}
fn app() -> App {
let mut app = App::new();
app.add_plugins(MinimalPlugins);
app.add_plugins(AlertsPlugin::new());
app
}
#[test]
fn test_one_error_system() {
let mut app = app();
app.add_systems(Update, alternate_output.anyhow_alert());
let mut query = app.world_mut().query::<&Alert>();
assert_eq!(query.iter(app.world()).count(), 0);
app.update();
assert_eq!(query.iter(app.world()).count(), 0);
app.update();
assert_eq!(query.iter(app.world()).count(), 1);
app.update();
assert_eq!(query.iter(app.world()).count(), 1);
app.update();
assert_eq!(query.iter(app.world()).count(), 2);
}
#[test]
fn test_error_collecting_system() {
let mut app = app();
app.add_systems(Update, alternate_output_many_errors.anyhow_alerts());
let mut query = app.world_mut().query::<&Alert>();
assert_eq!(query.iter(app.world()).count(), 0);
app.update();
assert_eq!(query.iter(app.world()).count(), 0);
app.update();
assert_eq!(query.iter(app.world()).count(), 1);
app.update();
assert_eq!(query.iter(app.world()).count(), 1);
app.update();
assert_eq!(query.iter(app.world()).count(), 2);
}
}