use std::collections::BTreeMap; use std::error::Error; use std::fmt; use toml::value::Table; use modules::{Module, EventLoop}; use modules::discord::DiscordModule; use modules::lua::LuaModule; use modules::stdin::StdinModule; use modules::echo::EchoModule; use modules::random::RandomModule; use modules::pvn::PvnModule; use modules::echobox::EchoboxModule; use modules::autolink::AutolinkModule; use modules::logger::LoggerModule; use modules::irc::IrcHandler; use std::sync::{Arc, Mutex}; pub struct ModuleLoader { types: BTreeMap<&'static str, fn(&Table, &Table) -> Box> } impl ModuleLoader { pub fn new () -> ModuleLoader { let mut types = BTreeMap::new(); types.insert("discord", DiscordModule::new as fn(&Table, &Table) -> Box); types.insert("lua", LuaModule::new as fn(&Table, &Table) -> Box); types.insert("stdin", StdinModule::new as fn(&Table, &Table) -> Box); types.insert("echo", EchoModule::new as fn(&Table, &Table) -> Box); types.insert("random", RandomModule::new as fn(&Table, &Table) -> Box); types.insert("pvn", PvnModule::new as fn(&Table, &Table) -> Box); types.insert("echobox", EchoboxModule::new as fn(&Table, &Table) -> Box); types.insert("autolink", AutolinkModule::new as fn(&Table, &Table) -> Box); types.insert("logger", LoggerModule::new as fn(&Table, &Table) -> Box); types.insert("irc", IrcHandler::new as fn(&Table, &Table) -> Box); ModuleLoader { types: types } } pub fn load_from_configuration (&self, configuration: Table) -> Result, ModuleLoaderError> { let general_config = configuration.get("general") .and_then(|value| value.as_table()) .map(|value| value.clone()) .unwrap_or_else(|| BTreeMap::new()); configuration.into_iter().filter(|&(ref key, _)| { key != "general" }).map(|(key, value)| { match value.as_table() { Some(table) => { let module = self.load_single_module(&key, &general_config, table)?; Result::Ok((key, module)) }, None => Result::Err(ModuleLoaderError { message: format!("Bad configuration parameters for module instance: {}. Configuration for a Module must be a table.", key) }) } }).collect() } pub fn load_single_module (&self, name: &str, general_configuration: &Table, module_configuration: &Table) -> Result { /* * The Module type defaults to the instance name (in the tenquestionmarks configuration) * but can explicitly be set by using the special "type" parameter. */ let module_type: &str = module_configuration.get("type") .and_then(|value| value.as_str()) .unwrap_or(name); match self.types.get(module_type) { Some(constructor) => Result::Ok(Module { module_type: module_type.to_owned(), config: module_configuration.clone(), event_loop: constructor(general_configuration, module_configuration), sender: Mutex::new(None) }), None => Result::Err(ModuleLoaderError { message: format!("No such module type: {}", module_type) }) } } } #[derive(Debug)] pub struct ModuleLoaderError { message: String } impl Error for ModuleLoaderError { fn description(&self) -> &str { &self.message[..] } } impl fmt::Display for ModuleLoaderError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ModuleLoaderError: {}", self.message) } }