rename plugin -> module

This commit is contained in:
Adrian Malacoda 2017-02-16 01:05:33 -06:00
parent bdee07143b
commit 0a51c7294f
10 changed files with 135 additions and 135 deletions

View File

@ -5,9 +5,9 @@ extern crate discord;
use std::collections::BTreeMap;
use toml::Table;
mod plugins;
use plugins::Plugin;
use plugins::loader::{PluginLoader, PluginLoaderError};
mod modules;
use modules::Module;
use modules::loader::{ModuleLoader, ModuleLoaderError};
mod event;
use event::Event;
@ -17,54 +17,54 @@ use std::sync::mpsc;
use std::sync::mpsc::Sender;
pub struct Tenquestionmarks {
plugins: BTreeMap<String, Box<Plugin>>
modules: BTreeMap<String, Box<Module>>
}
impl Tenquestionmarks {
pub fn with_plugins (plugins: BTreeMap<String, Box<Plugin>>) -> Tenquestionmarks {
pub fn with_modules (modules: BTreeMap<String, Box<Module>>) -> Tenquestionmarks {
let tqm = Tenquestionmarks {
plugins: plugins
modules: modules
};
for (key, plugin) in &tqm.plugins {
plugin.register(&tqm);
for (key, module) in &tqm.modules {
module.register(&tqm);
}
tqm
}
pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, PluginLoaderError> {
let loader = PluginLoader::new();
let plugins = loader.load_from_configuration(configuration)?;
Result::Ok(Tenquestionmarks::with_plugins(plugins))
pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, ModuleLoaderError> {
let loader = ModuleLoader::new();
let modules = loader.load_from_configuration(configuration)?;
Result::Ok(Tenquestionmarks::with_modules(modules))
}
pub fn run (&self) {
crossbeam::scope(|scope| {
// Our event channel.
// Plugins push events to tenquestionmarks using this channel.
// Modules push events to tenquestionmarks using this channel.
let (ref sender, ref receiver) = mpsc::channel();
// Plugin event consumer threads.
// tenquestionmarks propagates all events to each plugin through these
// Module event consumer threads.
// tenquestionmarks propagates all events to each Module through these
// channels.
let senders: Vec<Sender<Event>> = self.plugins.values().map(|plugin| {
let senders: Vec<Sender<Event>> = self.modules.values().map(|module| {
let (sender, receiver) = mpsc::channel();
scope.spawn(move || plugin.consume_events(receiver));
scope.spawn(move || module.consume_events(receiver));
sender
}).collect();
// Plugin event producer threads.
// Each plugin will produce events which tenquestionmarks will push
// into all other plugins.
for plugin in self.plugins.values() {
let plugin_sender = sender.clone();
scope.spawn(move || plugin.produce_events(plugin_sender));
// Module event producer threads.
// Each Module will produce events which tenquestionmarks will push
// into all other Modules.
for module in self.modules.values() {
let module_sender = sender.clone();
scope.spawn(move || module.produce_events(module_sender));
}
// tenquestionmarks main event loop.
// tenquestionmarks receives events produced by plugins and pushes them
// into all other plugins
// tenquestionmarks receives events produced by Modules and pushes them
// into all other Modules
loop {
match receiver.recv() {
Ok(event) => {

View File

@ -2,7 +2,7 @@ use discord;
use discord::Discord;
use discord::model::Event;
use plugins::Plugin;
use modules::Module;
use toml::Table;
use event;
@ -14,17 +14,17 @@ use MessageSender;
use User;
use Channel;
pub struct DiscordPlugin {
pub struct DiscordModule {
token: String
}
impl DiscordPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
impl DiscordModule {
pub fn new (configuration: &Table) -> Box<Module> {
let token = configuration.get("token")
.and_then(|value| value.as_str())
.unwrap_or("");
Box::new(DiscordPlugin {
Box::new(DiscordModule {
token: String::from(token)
})
}
@ -41,7 +41,7 @@ impl MessageSender for DiscordMessageSender {
}
}
impl Plugin for DiscordPlugin {
impl Module for DiscordModule {
fn produce_events<'a>(&'a self, sender: Sender<event::Event>) {
let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Login failed"));
let (mut connection, _) = discord.connect().expect("Connection failed");

View File

@ -1,26 +1,26 @@
use plugins::Plugin;
use modules::Module;
use toml::Table;
use std::sync::mpsc::Receiver;
use event::Event;
pub struct EchoPlugin {
pub struct EchoModule {
prefix: String
}
impl EchoPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
impl EchoModule {
pub fn new (configuration: &Table) -> Box<Module> {
let prefix = configuration.get("prefix")
.and_then(|value| value.as_str())
.unwrap_or("!echo ");
Box::new(EchoPlugin {
Box::new(EchoModule {
prefix: String::from(prefix)
})
}
}
impl Plugin for EchoPlugin {
impl Module for EchoModule {
fn consume_events (&self, receiver: Receiver<Event>) {
loop {
match receiver.recv() {

View File

@ -1,4 +1,4 @@
use plugins::Plugin;
use modules::Module;
use toml::Table;
use Tenquestionmarks;
@ -8,7 +8,7 @@ pub struct Hello {
}
impl Hello {
pub fn new (configuration: &Table) -> Box<Plugin> {
pub fn new (configuration: &Table) -> Box<Module> {
let name = configuration.get("name")
.and_then(|value| value.as_str())
.unwrap_or("world");
@ -19,7 +19,7 @@ impl Hello {
}
}
impl Plugin for Hello {
impl Module for Hello {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {
println!("Hello, {}!", self.name);
}

74
src/modules/loader.rs Normal file
View File

@ -0,0 +1,74 @@
use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
use toml::Table;
use modules::Module;
use modules::hello::Hello;
use modules::discord::DiscordModule;
use modules::lua::LuaModule;
use modules::stdin::StdinModule;
use modules::echo::EchoModule;
pub struct ModuleLoader {
types: BTreeMap<&'static str, fn(&Table) -> Box<Module>>
}
impl ModuleLoader {
pub fn new () -> ModuleLoader {
let mut types = BTreeMap::new();
types.insert("hello", Hello::new as fn(&Table) -> Box<Module>);
types.insert("discord", DiscordModule::new as fn(&Table) -> Box<Module>);
types.insert("lua", LuaModule::new as fn(&Table) -> Box<Module>);
types.insert("stdin", StdinModule::new as fn(&Table) -> Box<Module>);
types.insert("echo", EchoModule::new as fn(&Table) -> Box<Module>);
ModuleLoader {
types: types
}
}
pub fn load_from_configuration (&self, configuration: Table) -> Result<BTreeMap<String, Box<Module>>, ModuleLoaderError> {
configuration.into_iter().map(|(key, value)| {
match value.as_table() {
Some(table) => {
let module = self.load_single_module(&key, 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, configuration: &Table) -> Result<Box<Module>, ModuleLoaderError> {
/*
* 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 = configuration.get("type")
.and_then(|value| value.as_str())
.unwrap_or(name);
match self.types.get(module_type) {
Some(constructor) => Result::Ok(constructor(configuration)),
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)
}
}

14
src/modules/lua.rs Normal file
View File

@ -0,0 +1,14 @@
use modules::Module;
use toml::Table;
pub struct LuaModule {
}
impl LuaModule {
pub fn new (configuration: &Table) -> Box<Module> {
Box::new(LuaModule {})
}
}
impl Module for LuaModule {}

View File

@ -11,7 +11,7 @@ use event::Event;
use std::sync::mpsc::{Sender, Receiver};
pub trait Plugin : Sync {
pub trait Module : Sync {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {}
fn consume_events (&self, receiver: Receiver<Event>) {}
fn produce_events<'a>(&'a self, sender: Sender<Event>) {}

View File

@ -1,6 +1,6 @@
use std::io;
use plugins::Plugin;
use modules::Module;
use toml::Table;
use User;
@ -10,7 +10,7 @@ use std::sync::Arc;
use std::sync::mpsc::Sender;
use event::Event;
pub struct StdinPlugin {}
pub struct StdinModule {}
pub struct StdinMessageSender {
name: String
@ -22,13 +22,13 @@ impl MessageSender for StdinMessageSender {
}
}
impl StdinPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
Box::new(StdinPlugin {})
impl StdinModule {
pub fn new (configuration: &Table) -> Box<Module> {
Box::new(StdinModule {})
}
}
impl Plugin for StdinPlugin {
impl Module for StdinModule {
fn produce_events<'a>(&'a self, sender: Sender<Event>) {
let user = User {
name: String::from("Dave"),

View File

@ -1,74 +0,0 @@
use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
use toml::Table;
use plugins::Plugin;
use plugins::hello::Hello;
use plugins::discord::DiscordPlugin;
use plugins::lua::LuaPlugin;
use plugins::stdin::StdinPlugin;
use plugins::echo::EchoPlugin;
pub struct PluginLoader {
types: BTreeMap<&'static str, fn(&Table) -> Box<Plugin>>
}
impl PluginLoader {
pub fn new () -> PluginLoader {
let mut types = BTreeMap::new();
types.insert("hello", Hello::new as fn(&Table) -> Box<Plugin>);
types.insert("discord", DiscordPlugin::new as fn(&Table) -> Box<Plugin>);
types.insert("lua", LuaPlugin::new as fn(&Table) -> Box<Plugin>);
types.insert("stdin", StdinPlugin::new as fn(&Table) -> Box<Plugin>);
types.insert("echo", EchoPlugin::new as fn(&Table) -> Box<Plugin>);
PluginLoader {
types: types
}
}
pub fn load_from_configuration (&self, configuration: Table) -> Result<BTreeMap<String, Box<Plugin>>, PluginLoaderError> {
configuration.into_iter().map(|(key, value)| {
match value.as_table() {
Some(table) => {
let plugin = self.load_single_plugin(&key, table)?;
Result::Ok((key, plugin))
},
None => Result::Err(PluginLoaderError { message: format!("Bad configuration parameters for plugin instance: {}. Configuration for a plugin must be a table.", key) })
}
}).collect()
}
pub fn load_single_plugin (&self, name: &str, configuration: &Table) -> Result<Box<Plugin>, PluginLoaderError> {
/*
* The plugin type defaults to the instance name (in the tenquestionmarks configuration)
* but can explicitly be set by using the special "type" parameter.
*/
let plugin_type: &str = configuration.get("type")
.and_then(|value| value.as_str())
.unwrap_or(name);
match self.types.get(plugin_type) {
Some(constructor) => Result::Ok(constructor(configuration)),
None => Result::Err(PluginLoaderError { message: format!("No such plugin type: {}", plugin_type) })
}
}
}
#[derive(Debug)]
pub struct PluginLoaderError {
message: String
}
impl Error for PluginLoaderError {
fn description(&self) -> &str {
&self.message[..]
}
}
impl fmt::Display for PluginLoaderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PluginLoaderError: {}", self.message)
}
}

View File

@ -1,14 +0,0 @@
use plugins::Plugin;
use toml::Table;
pub struct LuaPlugin {
}
impl LuaPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
Box::new(LuaPlugin {})
}
}
impl Plugin for LuaPlugin {}