From 26a6b7763213d6fbb35983c5ae9d438cc78f0ef2 Mon Sep 17 00:00:00 2001 From: Adrian Malacoda Date: Mon, 13 Feb 2017 00:22:06 -0600 Subject: [PATCH] Flesh out plugins event handling, add example stdin plugin (event producer) and echo plugin (event consumer). Next step: fleshing out user/channel structs --- Cargo.toml | 1 + src/event/mod.rs | 8 +++++ src/lib.rs | 68 ++++++++++++++++++++++++++++++++++++------ src/main.rs | 5 +++- src/plugins/discord.rs | 6 ++-- src/plugins/echo.rs | 34 +++++++++++++++++++++ src/plugins/hello.rs | 7 +++-- src/plugins/loader.rs | 4 +++ src/plugins/lua.rs | 6 ++-- src/plugins/mod.rs | 11 ++++++- src/plugins/stdin.rs | 45 ++++++++++++++++++++++++++++ tenquestionmarks.toml | 4 +++ 12 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 src/event/mod.rs create mode 100644 src/plugins/echo.rs create mode 100644 src/plugins/stdin.rs diff --git a/Cargo.toml b/Cargo.toml index 1cdbf4d..3b00592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ authors=["Adrian Malacoda "] hlua = "0.3" discord = "0.7.0" toml = "0.2.1" +crossbeam = "0.2" diff --git a/src/event/mod.rs b/src/event/mod.rs new file mode 100644 index 0000000..991e828 --- /dev/null +++ b/src/event/mod.rs @@ -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 } +} diff --git a/src/lib.rs b/src/lib.rs index 8116cb3..99784b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,18 @@ extern crate toml; +extern crate crossbeam; use std::collections::BTreeMap; use toml::Table; mod plugins; use plugins::Plugin; -use plugins::loader::PluginLoader; -use plugins::loader::PluginLoaderError; +use plugins::loader::{PluginLoader, PluginLoaderError}; + +mod event; +use event::Event; + +use std::sync::mpsc; +use std::sync::mpsc::{Sender, Receiver}; pub struct Tenquestionmarks { plugins: BTreeMap> @@ -14,9 +20,15 @@ pub struct Tenquestionmarks { impl Tenquestionmarks { pub fn with_plugins (plugins: BTreeMap>) -> Tenquestionmarks { - Tenquestionmarks { + let tqm = Tenquestionmarks { plugins: plugins + }; + + for (key, plugin) in &tqm.plugins { + plugin.register(&tqm); } + + tqm } pub fn from_configuration (configuration: Table) -> Result { @@ -24,20 +36,58 @@ impl Tenquestionmarks { let plugins = loader.load_from_configuration(configuration)?; 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> = 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 { name: String, description: String, topic: String } +#[derive(Debug, Clone)] pub struct User { name: String } - -pub struct Message { - sender: String, - target: String, - content: String -} diff --git a/src/main.rs b/src/main.rs index 04585c9..9cd9c7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,10 @@ fn main () { Some(configuration) => { println!("Loaded configuration from: {}", configFileName); 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) } }, diff --git a/src/plugins/discord.rs b/src/plugins/discord.rs index 456f6d4..37dc31a 100644 --- a/src/plugins/discord.rs +++ b/src/plugins/discord.rs @@ -1,6 +1,8 @@ use plugins::Plugin; use toml::Table; +use Tenquestionmarks; + pub struct DiscordPlugin { } @@ -16,6 +18,4 @@ impl DiscordPlugin { } } -impl Plugin for DiscordPlugin { - -} +impl Plugin for DiscordPlugin {} diff --git a/src/plugins/echo.rs b/src/plugins/echo.rs new file mode 100644 index 0000000..ae84790 --- /dev/null +++ b/src/plugins/echo.rs @@ -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 { + 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); + } + } + _ => () + } + } +} diff --git a/src/plugins/hello.rs b/src/plugins/hello.rs index c676948..72716ce 100644 --- a/src/plugins/hello.rs +++ b/src/plugins/hello.rs @@ -1,6 +1,8 @@ use plugins::Plugin; use toml::Table; +use Tenquestionmarks; + pub struct Hello { name: String } @@ -11,7 +13,6 @@ impl Hello { .and_then(|value| value.as_str()) .unwrap_or("world"); - println!("Hello, {}!", name); Box::new(Hello { name: String::from(name) }) @@ -19,5 +20,7 @@ impl Hello { } impl Plugin for Hello { - + fn register (&self, tenquestionmarks: &Tenquestionmarks) { + println!("Hello, {}!", self.name); + } } diff --git a/src/plugins/loader.rs b/src/plugins/loader.rs index 5af2115..e9f15c6 100644 --- a/src/plugins/loader.rs +++ b/src/plugins/loader.rs @@ -8,6 +8,8 @@ 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> @@ -19,6 +21,8 @@ impl PluginLoader { types.insert("hello", Hello::new as fn(&Table) -> Box); types.insert("discord", DiscordPlugin::new as fn(&Table) -> Box); types.insert("lua", LuaPlugin::new as fn(&Table) -> Box); + types.insert("stdin", StdinPlugin::new as fn(&Table) -> Box); + types.insert("echo", EchoPlugin::new as fn(&Table) -> Box); PluginLoader { types: types } diff --git a/src/plugins/lua.rs b/src/plugins/lua.rs index 2c196ff..92dc958 100644 --- a/src/plugins/lua.rs +++ b/src/plugins/lua.rs @@ -1,6 +1,8 @@ use plugins::Plugin; use toml::Table; +use Tenquestionmarks; + pub struct LuaPlugin { } @@ -11,6 +13,4 @@ impl LuaPlugin { } } -impl Plugin for LuaPlugin { - -} +impl Plugin for LuaPlugin {} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index c5e9e68..c726baf 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -6,9 +6,18 @@ use toml::Table; pub mod hello; pub mod lua; pub mod discord; +pub mod stdin; +pub mod echo; 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) {} } diff --git a/src/plugins/stdin.rs b/src/plugins/stdin.rs new file mode 100644 index 0000000..ab5c9b7 --- /dev/null +++ b/src/plugins/stdin.rs @@ -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 { + Box::new(StdinPlugin { + user: User { + name: String::from("Dave") + } + }) + } +} + +impl Plugin for StdinPlugin { + fn register (&self, tenquestionmarks: &Tenquestionmarks) { + + } + + fn run (&self, sender: Sender) { + 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), + } + } + } +} diff --git a/tenquestionmarks.toml b/tenquestionmarks.toml index 027fbec..42d0621 100644 --- a/tenquestionmarks.toml +++ b/tenquestionmarks.toml @@ -10,3 +10,7 @@ name = "Fred" [discord] channel = "#testchannelpleaseignore" + +[stdin] + +[echo]