my-ecs 0.1.2

An Entity Component System (ECS) library
Documentation
//! ECS core module.

pub(crate) mod cache;
pub(crate) mod cmd;
pub(crate) mod ent;
pub(crate) mod entry;
pub(crate) mod lock;
pub(crate) mod post;
pub(crate) mod resource;
pub(crate) mod sched;
pub(crate) mod sys;
pub(crate) mod wait;
pub(crate) mod web;
pub(crate) mod worker;

pub mod prelude {
    pub use super::{
        cmd::prelude::*,
        ent::prelude::*,
        entry::prelude::*,
        post::prelude::*,
        resource::prelude::*,
        sched::prelude::*,
        sys::prelude::*,
        worker::prelude::*,
        {DynResult, EcsError},
    };
}

use std::{error::Error, fmt};
use thiserror::Error;

/// A [`Result`] with super flexible error type `Box<dyn Error + Send + Sync + 'static>`.
pub type DynResult<T> = Result<T, Box<dyn Error + Send + Sync + 'static>>;

/// An error generated by ECS instance.
#[derive(Error)]
#[repr(C)]
pub enum EcsError<Data = ()> {
    #[error("unknown system `{0}`")]
    UnknownSystem(String, Data),

    #[error("unknown entity `{0}`")]
    UnknownEntity(String, Data),
    #[error("invalid entity `{0}`")]
    InvalidEntity(String, Data),

    #[error("unknown resource `{0}`")]
    UnknownResource(String, Data),
    #[error("duplicated resource `{0}`")]
    DupResource(String, Data),

    #[error("invalid request `{0}`")]
    InvalidRequest(String, Data),

    #[error("unknown error `{0}`")]
    Unknown(String, Data),
}

impl<Data> fmt::Debug for EcsError<Data> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UnknownSystem(reason, ..) => {
                write!(f, "EcsError::UnknownSystem({reason}, ..)")
            }
            Self::UnknownEntity(reason, ..) => {
                write!(f, "EcsError::UnknownEntity({reason}, ..)")
            }
            Self::InvalidEntity(reason, ..) => {
                write!(f, "EcsError::InvalidEntity({reason}, ..)")
            }
            Self::UnknownResource(reason, ..) => {
                write!(f, "EcsError::UnknownResource({reason}, ..)")
            }
            Self::DupResource(reason, ..) => {
                write!(f, "EcsError::DupResource({reason}, ..)")
            }
            Self::InvalidRequest(reason, ..) => {
                write!(f, "EcsError::InvalidRequest({reason}, ..)")
            }
            Self::Unknown(reason, ..) => {
                write!(f, "EcsError::Unknown({reason}, ..)")
            }
        }
    }
}

impl<Data> EcsError<Data> {
    pub fn reason(&self) -> &str {
        match self {
            Self::UnknownSystem(reason, ..) => reason,
            Self::UnknownEntity(reason, ..) => reason,
            Self::InvalidEntity(reason, ..) => reason,
            Self::UnknownResource(reason, ..) => reason,
            Self::DupResource(reason, ..) => reason,
            Self::InvalidRequest(reason, ..) => reason,
            Self::Unknown(reason, ..) => reason,
        }
    }

    pub fn take_data(self) -> Data {
        match self {
            Self::UnknownSystem(_, data) => data,
            Self::UnknownEntity(_, data) => data,
            Self::InvalidEntity(_, data) => data,
            Self::UnknownResource(_, data) => data,
            Self::DupResource(_, data) => data,
            Self::InvalidRequest(_, data) => data,
            Self::Unknown(_, data) => data,
        }
    }

    pub fn without_data(self) -> EcsError<()> {
        self.with_data(())
    }

    pub fn with_data<OutData>(self, data: OutData) -> EcsError<OutData> {
        self.map_data(|_| data)
    }

    pub fn map_data<F, OutData>(self, f: F) -> EcsError<OutData>
    where
        F: FnOnce(Data) -> OutData,
    {
        match self {
            Self::UnknownSystem(reason, old) => EcsError::UnknownSystem(reason, f(old)),
            Self::UnknownEntity(reason, old) => EcsError::UnknownEntity(reason, f(old)),
            Self::InvalidEntity(reason, old) => EcsError::InvalidEntity(reason, f(old)),
            Self::UnknownResource(reason, old) => EcsError::UnknownResource(reason, f(old)),
            Self::DupResource(reason, old) => EcsError::DupResource(reason, f(old)),
            Self::InvalidRequest(reason, old) => EcsError::InvalidRequest(reason, f(old)),
            Self::Unknown(reason, old) => EcsError::Unknown(reason, f(old)),
        }
    }
}

pub mod stat {
    macro_rules! decl_counter {
        ($name:ident, $id:ident) => {
            paste::paste! {
                #[cfg(feature = "stat")]
                static $id: std::sync::atomic::AtomicIsize = std::sync::atomic::AtomicIsize::new(0);

                pub fn [<load _$name>]() -> isize {
                    #[cfg(feature = "stat")]
                    { $id.load(std::sync::atomic::Ordering::Relaxed) }

                    #[cfg(not(feature = "stat"))]
                    { -1 }
                }

                pub fn [<store _$name>](_value: isize) {
                    #[cfg(feature = "stat")]
                    $id.store(_value, std::sync::atomic::Ordering::Relaxed);
                }

                pub fn [<increase _$name>]() {
                    #[cfg(feature = "stat")]
                    $id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
                }

                pub fn [<assert_eq _$name>](_right: isize) {
                    #[cfg(feature = "stat")]
                    { assert_eq!([<load _$name>](), _right); }
                }

                pub fn [<assert_ne _$name>](_right: isize) {
                    #[cfg(feature = "stat")]
                    { assert_ne!([<load _$name>](), _right); }
                }
            }
        };
    }

    decl_counter!(system_task_count, SYS_CNT);
    decl_counter!(future_task_count, FUT_CNT);
    decl_counter!(parallel_task_count, PAR_CNT);
}