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

View File

@ -2,7 +2,7 @@ use discord;
use discord::Discord; use discord::Discord;
use discord::model::Event; use discord::model::Event;
use plugins::Plugin; use modules::Module;
use toml::Table; use toml::Table;
use event; use event;
@ -14,17 +14,17 @@ use MessageSender;
use User; use User;
use Channel; use Channel;
pub struct DiscordPlugin { pub struct DiscordModule {
token: String token: String
} }
impl DiscordPlugin { impl DiscordModule {
pub fn new (configuration: &Table) -> Box<Plugin> { pub fn new (configuration: &Table) -> Box<Module> {
let token = configuration.get("token") let token = configuration.get("token")
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
.unwrap_or(""); .unwrap_or("");
Box::new(DiscordPlugin { Box::new(DiscordModule {
token: String::from(token) 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>) { fn produce_events<'a>(&'a self, sender: Sender<event::Event>) {
let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Login failed")); let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Login failed"));
let (mut connection, _) = discord.connect().expect("Connection 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 toml::Table;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use event::Event; use event::Event;
pub struct EchoPlugin { pub struct EchoModule {
prefix: String prefix: String
} }
impl EchoPlugin { impl EchoModule {
pub fn new (configuration: &Table) -> Box<Plugin> { pub fn new (configuration: &Table) -> Box<Module> {
let prefix = configuration.get("prefix") let prefix = configuration.get("prefix")
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
.unwrap_or("!echo "); .unwrap_or("!echo ");
Box::new(EchoPlugin { Box::new(EchoModule {
prefix: String::from(prefix) prefix: String::from(prefix)
}) })
} }
} }
impl Plugin for EchoPlugin { impl Module for EchoModule {
fn consume_events (&self, receiver: Receiver<Event>) { fn consume_events (&self, receiver: Receiver<Event>) {
loop { loop {
match receiver.recv() { match receiver.recv() {

View File

@ -1,4 +1,4 @@
use plugins::Plugin; use modules::Module;
use toml::Table; use toml::Table;
use Tenquestionmarks; use Tenquestionmarks;
@ -8,7 +8,7 @@ pub struct Hello {
} }
impl Hello { impl Hello {
pub fn new (configuration: &Table) -> Box<Plugin> { pub fn new (configuration: &Table) -> Box<Module> {
let name = configuration.get("name") let name = configuration.get("name")
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
.unwrap_or("world"); .unwrap_or("world");
@ -19,7 +19,7 @@ impl Hello {
} }
} }
impl Plugin for Hello { impl Module for Hello {
fn register (&self, tenquestionmarks: &Tenquestionmarks) { fn register (&self, tenquestionmarks: &Tenquestionmarks) {
println!("Hello, {}!", self.name); 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}; use std::sync::mpsc::{Sender, Receiver};
pub trait Plugin : Sync { pub trait Module : Sync {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {} fn register (&self, tenquestionmarks: &Tenquestionmarks) {}
fn consume_events (&self, receiver: Receiver<Event>) {} fn consume_events (&self, receiver: Receiver<Event>) {}
fn produce_events<'a>(&'a self, sender: Sender<Event>) {} fn produce_events<'a>(&'a self, sender: Sender<Event>) {}

View File

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