algosul-core 0.0.16

Some user-friendly yet personalized tools
//! # Modules
//!
//! ## Module Manager
//!
//! + `register_module`
//!   + return:
//!     + [ModuleID]

use core::fmt::{Debug, Formatter};
use std::collections::HashMap;

use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error
{
  #[error("Failed to new Module ID")]
  FailedToNewModuleID,
  #[error("Non-existent Module ID")]
  NonExistentModuleID,
}

pub type Result<T> = core::result::Result<T, Error>;

/// Module Manager
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct ModuleManager
{
  id:      ModuleID,
  modules: HashMap<ModuleID, ModuleWrapper>,
}

/// Module ID
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ModuleID(usize);

/// Module Data
pub trait Module: 'static
{
  fn name(&self) -> &str;
}

pub struct ModuleWrapper
{
  inner: Box<dyn Module>,
}

impl ModuleID
{
  const MAX_MODULE_ID: usize = u32::MAX as usize;

  fn first() -> Self
  {
    Self(0)
  }

  fn next(&mut self) -> Self
  {
    if self.0 == Self::MAX_MODULE_ID
    {
      panic!("Module ID exhausted");
    }
    let old = *self;
    *self = Self(self.0 + 1);
    old
  }
}

impl ModuleWrapper
{
  fn new(inner: impl Module) -> Self
  {
    Self { inner: Box::new(inner) }
  }
}

impl AsRef<dyn Module> for ModuleWrapper
{
  fn as_ref(&self) -> &dyn Module
  {
    self.inner.as_ref()
  }
}

impl AsMut<dyn Module> for ModuleWrapper
{
  fn as_mut(&mut self) -> &mut dyn Module
  {
    self.inner.as_mut()
  }
}

impl ModuleManager
{
  pub fn new() -> Result<Self>
  {
    Ok(Self { id: ModuleID::first(), modules: HashMap::new() })
  }

  pub fn register_module(&mut self, module: impl Module) -> ModuleID
  {
    let id = self.id.next();
    self.modules.insert(id, ModuleWrapper::new(module));
    id
  }

  pub fn unregister_module(&mut self, module: ModuleID) -> Result<()>
  {
    let _ = self.modules.remove(&module).ok_or(Error::NonExistentModuleID)?;
    Ok(())
  }

  pub fn get(&self, id: ModuleID) -> Option<&ModuleWrapper>
  {
    self.modules.get(&id)
  }

  pub fn get_mut(&mut self, id: ModuleID) -> Option<&mut ModuleWrapper>
  {
    self.modules.get_mut(&id)
  }
}

#[cfg(debug_assertions)]
impl Debug for ModuleWrapper
{
  fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result
  {
    f.debug_struct(stringify!(Module))
      .field("name", &self.inner.name())
      .finish()
  }
}