#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![warn(clippy::pedantic)]
use bevy_time::Time;
use bevy_utils::Duration;
use std::hash::Hash;
pub use bevy_mod_sysfail_macros::*;
pub use logged_errors::LoggedErrors;
mod logged_errors;
#[doc(hidden)]
pub mod __macro {
pub type LoggedErrorsParam<'s, T> = bevy_ecs::prelude::Local<'s, LoggedErrors<T>>;
pub type TimeParam<'s> = bevy_ecs::prelude::Res<'s, bevy_time::Time>;
pub use crate::{Failure, LoggedErrors};
pub use bevy_log::{debug, error, info, trace, warn};
}
#[deprecated(
since = "4.0.0",
note = "Directly import the macros with bevy_mod_sysfail::{sysfail, quick_sysfail}"
)]
pub mod macros {
pub use crate::{quick_sysfail, sysfail};
}
pub mod traits {
#[allow(deprecated)]
pub use crate::{Failure, FailureMode, LogLevelOverride};
}
#[deprecated(since = "4.0.0", note = "runtime log levels are not respected anymore")]
pub struct OverrideLevel<T> {
inner: T,
}
#[allow(deprecated)]
impl<T> OverrideLevel<T> {
pub fn new(_: LogLevel, inner: T) -> Self {
Self { inner }
}
}
#[allow(deprecated)]
impl<T: FailureMode> FailureMode for OverrideLevel<T> {
fn log_level(&self) -> LogLevel {
LogLevel::Silent
}
type ID = T::ID;
fn identify(&self) -> Self::ID {
self.inner.identify()
}
fn display(&self) -> Option<String> {
None
}
fn cooldown(&self) -> Duration {
self.inner.cooldown()
}
}
#[deprecated(since = "4.0.0", note = "runtime log levels are not respected anymore")]
#[allow(deprecated)]
pub trait LogLevelOverride: Sized {
type Output;
fn set_level(self, level: LogLevel) -> Self::Output;
fn silent(self) -> Self::Output {
self.set_level(LogLevel::Silent)
}
fn trace(self) -> Self::Output {
self.set_level(LogLevel::Trace)
}
fn debug(self) -> Self::Output {
self.set_level(LogLevel::Debug)
}
fn info(self) -> Self::Output {
self.set_level(LogLevel::Info)
}
fn warn(self) -> Self::Output {
self.set_level(LogLevel::Warn)
}
fn error(self) -> Self::Output {
self.set_level(LogLevel::Error)
}
}
#[allow(deprecated)]
impl<T: FailureMode> LogLevelOverride for T {
type Output = OverrideLevel<Self>;
fn set_level(self, level: LogLevel) -> OverrideLevel<Self> {
OverrideLevel::new(level, self)
}
}
#[allow(deprecated)]
impl<T: FailureMode> LogLevelOverride for Result<(), T> {
type Output = Result<(), OverrideLevel<T>>;
fn set_level(self, level: LogLevel) -> Self::Output {
self.map_err(|err| err.set_level(level))
}
}
#[deprecated(since = "4.0.0", note = "This is unusued and serves no purpose")]
#[derive(Debug, Clone, Copy)]
pub enum LogLevel {
Silent,
Trace,
Debug,
Info,
Warn,
Error,
}
pub trait FailureMode {
#[deprecated(since = "4.0.0", note = "This is ignored")]
#[allow(deprecated)]
fn log_level(&self) -> LogLevel {
LogLevel::Error
}
fn cooldown(&self) -> Duration {
Duration::from_secs(1)
}
type ID: Hash + Eq;
fn identify(&self) -> Self::ID;
#[deprecated(
since = "4.0.0",
note = "This crate now directly uses the fmt::Display impl on the error."
)]
fn display(&self) -> Option<String> {
None
}
fn log(&self) {}
}
impl FailureMode for () {
#[allow(deprecated)]
fn log_level(&self) -> LogLevel {
LogLevel::Silent
}
type ID = Self;
fn identify(&self) {}
fn display(&self) -> Option<String> {
None
}
}
impl FailureMode for &'static str {
#[allow(deprecated)]
fn log_level(&self) -> LogLevel {
LogLevel::Warn
}
type ID = Self;
fn identify(&self) -> Self {
self
}
fn display(&self) -> Option<String> {
Some((*self).to_string())
}
}
impl FailureMode for Box<dyn std::error::Error> {
#[allow(deprecated)]
fn log_level(&self) -> LogLevel {
LogLevel::Warn
}
type ID = ();
fn identify(&self) {}
fn display(&self) -> Option<String> {
Some(self.to_string())
}
}
impl FailureMode for anyhow::Error {
#[allow(deprecated)]
fn log_level(&self) -> LogLevel {
LogLevel::Warn
}
type ID = ();
fn identify(&self) {}
fn display(&self) -> Option<String> {
Some(self.to_string())
}
}
pub trait Failure {
type Error: FailureMode;
fn failure(self) -> Option<Self::Error>;
fn get_error(self, time: &Time, logged_errors: &mut LoggedErrors<Self>) -> Option<Self::Error>
where
Self: Sized,
{
let error = self.failure()?;
let cooldown = error.cooldown();
let now = time.elapsed();
let last_shown = logged_errors.0.insert(error.identify(), now);
let should_log = last_shown.map_or(true, |d| now < d + cooldown);
should_log.then_some(error)
}
}
impl<T: FailureMode> Failure for Result<(), T> {
type Error = T;
fn failure(self) -> Option<Self::Error> {
self.err()
}
}
impl Failure for Option<()> {
type Error = &'static str;
fn failure(self) -> Option<Self::Error> {
Some("A none value")
}
}