bean-script 0.2.12

Simple scripting language for easy use in other projects.
Documentation
use std::{any::Any, cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};

use crate::{
	logger::Logger,
	util::{make_ref, MutRc},
};

use super::{bean_std, BuiltinModule, CustomModule, Module, ModuleBuilder};

pub(super) enum RegistryEntry {
	Uninitialized(Box<dyn FnOnce() -> MutRc<dyn Module>>),
	Available(MutRc<dyn Module>),
}

impl RegistryEntry {
	pub fn get_or_init(self) -> MutRc<dyn Module> {
		match self {
			Self::Uninitialized(init) => init(),
			Self::Available(v) => v,
		}
	}
}

#[derive(Clone, Copy)]
pub struct RegistryFeatures {
	pub custom_modules: bool,
	pub import: bool,
	pub lang_debug: bool,
}

impl Default for RegistryFeatures {
	fn default() -> Self {
		Self {
			custom_modules: true,
			import: true,
			lang_debug: false,
		}
	}
}

pub struct ModuleRegistry {
	pub(super) registered: HashMap<String, RegistryEntry>,
	pub(super) local: HashMap<PathBuf, MutRc<CustomModule>>,
	pub(super) loading: Vec<PathBuf>,
	runtime: MutRc<BuiltinModule>,
	pub features: RegistryFeatures,
	pub logger: Logger,
	pub metadata: HashMap<String, Box<dyn Any>>,
}

impl std::fmt::Debug for ModuleRegistry {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("ModuleRegistry").finish()
	}
}

impl ModuleRegistry {
	pub fn new(features: RegistryFeatures) -> Self {
		let standard_lib = BuiltinModule::new(bean_std::construct, features);
		let mut s = Self {
			registered: HashMap::new(),
			local: HashMap::new(),
			loading: Vec::new(),
			runtime: match RefCell::borrow(
				&standard_lib.get_submodule("runtime").unwrap(),
			)
			.as_any()
			.downcast_ref::<BuiltinModule>()
			{
				Some(r) => Rc::new(RefCell::new(r.clone())),
				None => panic!("Runtime module is custom?"),
			},
			features: RegistryFeatures::default(),
			logger: Logger::Stdout,
			metadata: HashMap::new(),
		};
		s.registered.insert(
			String::from("std"),
			RegistryEntry::Available(make_ref(standard_lib)),
		);
		s
	}

	pub fn register_builtin(
		&mut self,
		name: String,
		constructor: fn(&mut ModuleBuilder),
	) {
		if !self.registered.contains_key(&name) {
			let features = self.features.clone();
			self.registered.insert(
				name,
				RegistryEntry::Uninitialized(Box::new(move || {
					make_ref(BuiltinModule::new(constructor, features))
				})),
			);
		} else {
			panic!("Trying to register a builtin module under a used name.")
		}
	}

	pub fn register_initialized_builtin(&mut self, name: String, module: BuiltinModule) {
		if !self.registered.contains_key(&name) {
			self.registered
				.insert(name, RegistryEntry::Available(make_ref(module)));
		} else {
			panic!("Trying to register a builtin module under a used name.")
		}
	}

	pub fn set_logger(&mut self, logger: Logger) {
		self.logger = logger;
	}

	pub fn runtime(&self) -> MutRc<BuiltinModule> {
		Rc::clone(&self.runtime)
	}
}