Flesh out plugins event handling, add example stdin plugin (event producer) and echo plugin (event consumer). Next step: fleshing out user/channel structs

This commit is contained in:
Adrian Malacoda 2017-02-13 00:22:06 -06:00
parent a31b060dd3
commit 26a6b77632
12 changed files with 180 additions and 19 deletions

View File

@ -7,3 +7,4 @@ authors=["Adrian Malacoda <adrian.malacoda@monarch-pass.net>"]
hlua = "0.3" hlua = "0.3"
discord = "0.7.0" discord = "0.7.0"
toml = "0.2.1" toml = "0.2.1"
crossbeam = "0.2"

8
src/event/mod.rs Normal file
View File

@ -0,0 +1,8 @@
use {Channel, User};
#[derive(Debug, Clone)]
pub enum Event<'a> {
Message { sender: &'a User, channel: Option<&'a Channel>, content: String },
Join { channel: &'a Channel },
Quit { channel: &'a Channel }
}

View File

@ -1,12 +1,18 @@
extern crate toml; extern crate toml;
extern crate crossbeam;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use toml::Table; use toml::Table;
mod plugins; mod plugins;
use plugins::Plugin; use plugins::Plugin;
use plugins::loader::PluginLoader; use plugins::loader::{PluginLoader, PluginLoaderError};
use plugins::loader::PluginLoaderError;
mod event;
use event::Event;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
pub struct Tenquestionmarks { pub struct Tenquestionmarks {
plugins: BTreeMap<String, Box<Plugin>> plugins: BTreeMap<String, Box<Plugin>>
@ -14,9 +20,15 @@ pub struct Tenquestionmarks {
impl Tenquestionmarks { impl Tenquestionmarks {
pub fn with_plugins (plugins: BTreeMap<String, Box<Plugin>>) -> Tenquestionmarks { pub fn with_plugins (plugins: BTreeMap<String, Box<Plugin>>) -> Tenquestionmarks {
Tenquestionmarks { let tqm = Tenquestionmarks {
plugins: plugins plugins: plugins
};
for (key, plugin) in &tqm.plugins {
plugin.register(&tqm);
} }
tqm
} }
pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, PluginLoaderError> { pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, PluginLoaderError> {
@ -24,20 +36,58 @@ impl Tenquestionmarks {
let plugins = loader.load_from_configuration(configuration)?; let plugins = loader.load_from_configuration(configuration)?;
Result::Ok(Tenquestionmarks::with_plugins(plugins)) Result::Ok(Tenquestionmarks::with_plugins(plugins))
} }
pub fn run (&self) {
crossbeam::scope(|scope| {
// Our event channel.
// Plugins push events to tenquestionmarks using this channel.
let (ref sender, ref receiver) = mpsc::channel();
// Plugin event channels.
// tenquestionmarks propagates all events to each plugin through these
// channels.
let senders: Vec<Sender<Event>> = self.plugins.values().map(|plugin| {
let (sender, receiver) = mpsc::channel();
scope.spawn(move || {
loop {
match receiver.recv() {
Ok(event) => { plugin.on_event(event) }
Err(error) => { println!("{:?}", error); }
}
}
});
sender
}).collect();
// Plugin main threads.
for plugin in self.plugins.values() {
let plugin_sender = sender.clone();
scope.spawn(move || plugin.run(plugin_sender));
}
// tenquestionmarks main event loop.
loop {
match receiver.recv() {
Ok(event) => {
for sender in &senders {
sender.send(event.clone());
}
},
Err(_) => {}
}
}
});
}
} }
#[derive(Debug, Clone)]
pub struct Channel { pub struct Channel {
name: String, name: String,
description: String, description: String,
topic: String topic: String
} }
#[derive(Debug, Clone)]
pub struct User { pub struct User {
name: String name: String
} }
pub struct Message {
sender: String,
target: String,
content: String
}

View File

@ -20,7 +20,10 @@ fn main () {
Some(configuration) => { Some(configuration) => {
println!("Loaded configuration from: {}", configFileName); println!("Loaded configuration from: {}", configFileName);
match Tenquestionmarks::from_configuration(configuration) { match Tenquestionmarks::from_configuration(configuration) {
Ok(tqm) => println!("loaded tenquestionmarks"), Ok(tqm) => {
println!("tenquestionmarks initialized successfully");
tqm.run();
},
Err(e) => println!("Failed to initialize tenquestionmarks: {:?}", e) Err(e) => println!("Failed to initialize tenquestionmarks: {:?}", e)
} }
}, },

View File

@ -1,6 +1,8 @@
use plugins::Plugin; use plugins::Plugin;
use toml::Table; use toml::Table;
use Tenquestionmarks;
pub struct DiscordPlugin { pub struct DiscordPlugin {
} }
@ -16,6 +18,4 @@ impl DiscordPlugin {
} }
} }
impl Plugin for DiscordPlugin { impl Plugin for DiscordPlugin {}
}

34
src/plugins/echo.rs Normal file
View File

@ -0,0 +1,34 @@
use plugins::Plugin;
use toml::Table;
use event::Event;
pub struct EchoPlugin {
prefix: String
}
impl EchoPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
let prefix = configuration.get("prefix")
.and_then(|value| value.as_str())
.unwrap_or("!echo ");
Box::new(EchoPlugin {
prefix: String::from(prefix)
})
}
}
impl Plugin for EchoPlugin {
fn on_event (&self, event: Event) {
match event {
Event::Message { content: message, channel: channel, sender: sender } => {
if message.starts_with(self.prefix.as_str()) {
let substring = String::from(&message[self.prefix.chars().count()..]);
println!("Echo: {:?}", substring);
}
}
_ => ()
}
}
}

View File

@ -1,6 +1,8 @@
use plugins::Plugin; use plugins::Plugin;
use toml::Table; use toml::Table;
use Tenquestionmarks;
pub struct Hello { pub struct Hello {
name: String name: String
} }
@ -11,7 +13,6 @@ impl Hello {
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
.unwrap_or("world"); .unwrap_or("world");
println!("Hello, {}!", name);
Box::new(Hello { Box::new(Hello {
name: String::from(name) name: String::from(name)
}) })
@ -19,5 +20,7 @@ impl Hello {
} }
impl Plugin for Hello { impl Plugin for Hello {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {
println!("Hello, {}!", self.name);
}
} }

View File

@ -8,6 +8,8 @@ use plugins::Plugin;
use plugins::hello::Hello; use plugins::hello::Hello;
use plugins::discord::DiscordPlugin; use plugins::discord::DiscordPlugin;
use plugins::lua::LuaPlugin; use plugins::lua::LuaPlugin;
use plugins::stdin::StdinPlugin;
use plugins::echo::EchoPlugin;
pub struct PluginLoader { pub struct PluginLoader {
types: BTreeMap<&'static str, fn(&Table) -> Box<Plugin>> types: BTreeMap<&'static str, fn(&Table) -> Box<Plugin>>
@ -19,6 +21,8 @@ impl PluginLoader {
types.insert("hello", Hello::new as fn(&Table) -> Box<Plugin>); types.insert("hello", Hello::new as fn(&Table) -> Box<Plugin>);
types.insert("discord", DiscordPlugin::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("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 { PluginLoader {
types: types types: types
} }

View File

@ -1,6 +1,8 @@
use plugins::Plugin; use plugins::Plugin;
use toml::Table; use toml::Table;
use Tenquestionmarks;
pub struct LuaPlugin { pub struct LuaPlugin {
} }
@ -11,6 +13,4 @@ impl LuaPlugin {
} }
} }
impl Plugin for LuaPlugin { impl Plugin for LuaPlugin {}
}

View File

@ -6,9 +6,18 @@ use toml::Table;
pub mod hello; pub mod hello;
pub mod lua; pub mod lua;
pub mod discord; pub mod discord;
pub mod stdin;
pub mod echo;
pub mod loader; pub mod loader;
pub trait Plugin { use Tenquestionmarks;
use event::Event;
use std::sync::mpsc::{Sender, Receiver};
pub trait Plugin : Sync {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {}
fn on_event (&self, event: Event) {}
fn run (&self, sender: Sender<Event>) {}
} }

45
src/plugins/stdin.rs Normal file
View File

@ -0,0 +1,45 @@
use std::io;
use plugins::Plugin;
use toml::Table;
use Tenquestionmarks;
use User;
use std::sync::mpsc::Sender;
use event::Event;
pub struct StdinPlugin {
user: User
}
impl StdinPlugin {
pub fn new (configuration: &Table) -> Box<Plugin> {
Box::new(StdinPlugin {
user: User {
name: String::from("Dave")
}
})
}
}
impl Plugin for StdinPlugin {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {
}
fn run (&self, sender: Sender<Event>) {
let user = &self.user;
loop {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(n) => {
let message = Event::Message { sender: user, content: input, channel: None };
sender.send(message);
}
Err(error) => println!("error: {}", error),
}
}
}
}

View File

@ -10,3 +10,7 @@ name = "Fred"
[discord] [discord]
channel = "#testchannelpleaseignore" channel = "#testchannelpleaseignore"
[stdin]
[echo]