use core::any::{Any, TypeId, type_name};
use core::fmt;
use std::collections::HashMap;
use std::sync::{Mutex, PoisonError};
use crate::generator::GenerateError;
use crate::node::{Node, NodeError};
type Defaults = HashMap<TypeId, Box<dyn Any + Send + Sync>>;
static DEFAULTS: Mutex<Option<Defaults>> = Mutex::new(None);
pub fn init<N: Node>(node: N) -> Result<(), InitError> {
if let Some(bits) = N::BITS {
node.pack(bits).map_err(InitError::InvalidNode)?;
}
let mut defaults = DEFAULTS.lock().unwrap_or_else(PoisonError::into_inner);
let defaults = defaults.get_or_insert_default();
if defaults.contains_key(&TypeId::of::<N>()) {
return Err(InitError::AlreadyInitialized);
}
defaults.insert(TypeId::of::<N>(), Box::new(node));
Ok(())
}
pub(crate) fn default_node<N: Node>() -> Result<N, GenerateError> {
let defaults = DEFAULTS.lock().unwrap_or_else(PoisonError::into_inner);
defaults
.as_ref()
.and_then(|defaults| defaults.get(&TypeId::of::<N>()))
.and_then(|node| node.downcast_ref::<N>())
.copied()
.ok_or(GenerateError::NotInitialized {
node: type_name::<N>(),
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum InitError {
InvalidNode(NodeError),
AlreadyInitialized,
}
impl fmt::Display for InitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidNode(_) => f.write_str("default node is not valid"),
Self::AlreadyInitialized => {
f.write_str("a default node of this type is already initialized")
}
}
}
}
impl std::error::Error for InitError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InvalidNode(error) => Some(error),
Self::AlreadyInitialized => None,
}
}
}