rename plugin -> module
This commit is contained in:
parent
bdee07143b
commit
0a51c7294f
50
src/lib.rs
50
src/lib.rs
@ -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) => {
|
||||
|
@ -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");
|
@ -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() {
|
@ -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
74
src/modules/loader.rs
Normal 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
14
src/modules/lua.rs
Normal 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 {}
|
@ -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>) {}
|
@ -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"),
|
@ -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)
|
||||
}
|
||||
}
|
@ -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 {}
|
Loading…
x
Reference in New Issue
Block a user