Simplify module trait by combining produce/consume event methods into a single run method that runs in the module's own thread and can produce and/or consume events. Introduce an Envelope struct that encapsulates event + to/from so we can (eventually) tag every event and also limit where events are sent (e.g. you can have a specific module configured to talk or listen only to a certain other module).

This commit is contained in:
Adrian Malacoda 2017-02-25 20:17:46 -06:00
parent 442b617f31
commit 37a9645f5b
11 changed files with 73 additions and 73 deletions

View File

@ -1,5 +1,7 @@
use {Message, Channel, User}; use {Message, Channel, User};
use std::sync::Arc;
pub enum Event { pub enum Event {
Message { message: Message }, // A user sends a message Message { message: Message }, // A user sends a message
@ -9,3 +11,19 @@ pub enum Event {
UserJoin { channel: Channel, user: User }, // A user joins a channel UserJoin { channel: Channel, user: User }, // A user joins a channel
UserQuit { channel: Channel, user: User } // A user quits a channel UserQuit { channel: Channel, user: User } // A user quits a channel
} }
pub struct Envelope {
pub from: Option<String>,
pub event: Arc<Event>,
pub to: Vec<String>
}
impl Envelope {
pub fn new (event: Event) -> Envelope {
Envelope {
from: None,
event: Arc::new(event),
to: vec![]
}
}
}

View File

@ -13,7 +13,7 @@ use modules::Module;
use modules::loader::{ModuleLoader, ModuleLoaderError}; use modules::loader::{ModuleLoader, ModuleLoaderError};
mod event; mod event;
use event::Event; use event::Envelope;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc; use std::sync::mpsc;
@ -34,7 +34,7 @@ impl Tenquestionmarks {
modules: modules modules: modules
}; };
for (key, module) in &tqm.modules { for (_, module) in &tqm.modules {
module.register(&tqm); module.register(&tqm);
} }
@ -53,39 +53,31 @@ impl Tenquestionmarks {
// Modules 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();
// Module event consumer threads. // Module threads.
// Each Module will produce events which tenquestionmarks will push
// into all other Modules.
// tenquestionmarks propagates all events to each Module through these // tenquestionmarks propagates all events to each Module through these
// channels. // channels.
let senders: BTreeMap<&str, Sender<Arc<Event>>> = self.modules.iter().map(|(key, module)| { let senders: BTreeMap<&str, Sender<Arc<Envelope>>> = self.modules.iter().map(|(key, module)| {
let module_sender = sender.clone();
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
info!("Spawning consumer thread for \"{}\"", key); info!("Spawning thread for \"{}\"", key);
scope.spawn(move || { scope.spawn(move || {
module.consume_events(receiver); module.run(module_sender, receiver);
info!("Consumer thread for \"{}\" is exiting", key); info!("Thread for \"{}\" is exiting", key);
}); });
(&key[..], sender) (&key[..], sender)
}).collect(); }).collect();
// Module event producer threads.
// Each Module will produce events which tenquestionmarks will push
// into all other Modules.
for (key, module) in self.modules.iter() {
let module_sender = sender.clone();
info!("Spawning producer thread for \"{}\"", key);
scope.spawn(move || {
module.produce_events(module_sender);
info!("Producer thread for \"{}\" is exiting", key);
});
}
// tenquestionmarks main event loop. // tenquestionmarks main event loop.
// tenquestionmarks receives events produced by Modules and pushes them // tenquestionmarks receives events produced by Modules and pushes them
// into all other Modules // into all other Modules
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(event) => { Ok(envelope) => {
let arc_envelope = Arc::new(envelope);
for (key, sender) in &senders { for (key, sender) in &senders {
match sender.send(event.clone()) { match sender.send(arc_envelope.clone()) {
Err(err) => debug!("Failed to dispatch event to module \"{}\": {:?}", key, err), Err(err) => debug!("Failed to dispatch event to module \"{}\": {:?}", key, err),
Ok(_) => {} Ok(_) => {}
} }
@ -138,5 +130,5 @@ impl User {
} }
pub trait MessageSender : Sync + Send { pub trait MessageSender : Sync + Send {
fn send_message (&self, message: &str) {} fn send_message (&self, _: &str) {}
} }

View File

@ -15,16 +15,16 @@ extern crate env_logger;
fn main () { fn main () {
env_logger::init().unwrap(); env_logger::init().unwrap();
let configFileName = env::args().nth(1).unwrap_or("tenquestionmarks.toml".into()); let config_file_name = env::args().nth(1).unwrap_or("tenquestionmarks.toml".into());
match File::open(&configFileName) { match File::open(&config_file_name) {
Ok(mut file) => { Ok(mut file) => {
let mut contents = String::new(); let mut contents = String::new();
match file.read_to_string(&mut contents) { match file.read_to_string(&mut contents) {
Ok(value) => { Ok(_) => {
let mut parser = Parser::new(&contents); let mut parser = Parser::new(&contents);
match parser.parse() { match parser.parse() {
Some(configuration) => { Some(configuration) => {
info!("Loaded configuration from: {}", configFileName); info!("Loaded configuration from: {}", config_file_name);
match Tenquestionmarks::from_configuration(configuration) { match Tenquestionmarks::from_configuration(configuration) {
Ok(tqm) => { Ok(tqm) => {
info!("tenquestionmarks initialized successfully"); info!("tenquestionmarks initialized successfully");
@ -34,12 +34,12 @@ fn main () {
} }
}, },
None => { None => {
error!("Failed to parse config file {}: {:?}. Config file must be a valid TOML file.", configFileName, parser.errors); error!("Failed to parse config file {}: {:?}. Config file must be a valid TOML file.", config_file_name, parser.errors);
} }
} }
}, },
Err(e) => { Err(e) => {
error!("Failed to open config file {}: {:?}", configFileName, e); error!("Failed to open config file {}: {:?}", config_file_name, e);
} }
} }
}, },

View File

@ -8,7 +8,7 @@ use toml::Table;
use event; use event;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Sender; use std::sync::mpsc::{Sender, Receiver};
use {MessageSender, Message, User, Channel}; use {MessageSender, Message, User, Channel};
@ -52,7 +52,7 @@ impl MessageSender for DiscordMessageSender {
} }
impl Module for DiscordModule { impl Module for DiscordModule {
fn produce_events<'a>(&'a self, sender: Sender<Arc<event::Event>>) { fn run (&self, sender: Sender<event::Envelope>, _: Receiver<Arc<event::Envelope>>) {
let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Discord module: Login failed")); let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Discord module: Login failed"));
let (mut connection, _) = discord.connect().expect("Discord module: Connection failed"); let (mut connection, _) = discord.connect().expect("Discord module: Connection failed");
@ -86,7 +86,7 @@ impl Module for DiscordModule {
channel: Some(channel) channel: Some(channel)
}; };
match sender.send(Arc::new(event::Event::Message { message: message })) { match sender.send(event::Envelope::new(event::Event::Message { message: message })) {
Err(err) => error!("Error sending message event: {:?}", err), Err(err) => error!("Error sending message event: {:?}", err),
Ok(_) => {} Ok(_) => {}
} }

View File

@ -2,10 +2,10 @@ use modules::Module;
use toml::Table; use toml::Table;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::{Sender, Receiver};
use helpers::command::split_command; use helpers::command::split_command;
use event::Event; use event::{Event, Envelope};
pub struct EchoModule { pub struct EchoModule {
prefix: String prefix: String
@ -24,11 +24,11 @@ impl EchoModule {
} }
impl Module for EchoModule { impl Module for EchoModule {
fn consume_events (&self, receiver: Receiver<Arc<Event>>) { fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(event) => { Ok(envelope) => {
match *event { match *envelope.event {
Event::Message { ref message } => { Event::Message { ref message } => {
debug!("Received message... {:?}", message.content); debug!("Received message... {:?}", message.content);
match split_command(&message.content) { match split_command(&message.content) {

View File

@ -2,10 +2,10 @@ use modules::Module;
use toml::Table; use toml::Table;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::{Sender, Receiver};
use helpers::command::split_command; use helpers::command::split_command;
use event::Event; use event::{Event, Envelope};
use echobox::Echobox; use echobox::Echobox;
pub struct EchoboxModule { pub struct EchoboxModule {
@ -31,13 +31,13 @@ impl EchoboxModule {
} }
impl Module for EchoboxModule { impl Module for EchoboxModule {
fn consume_events (&self, receiver: Receiver<Arc<Event>>) { fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
let echobox = Echobox::with_file(&self.file).unwrap(); let echobox = Echobox::with_file(&self.file).unwrap();
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(event) => { Ok(envelope) => {
match *event { match *envelope.event {
Event::Message { ref message } => { Event::Message { ref message } => {
debug!("Received message... {:?}", message.content); debug!("Received message... {:?}", message.content);
match split_command(&message.content) { match split_command(&message.content) {

View File

@ -6,7 +6,7 @@ pub struct LuaModule {
} }
impl LuaModule { impl LuaModule {
pub fn new (configuration: &Table) -> Box<Module> { pub fn new (_: &Table) -> Box<Module> {
Box::new(LuaModule {}) Box::new(LuaModule {})
} }
} }

View File

@ -9,13 +9,12 @@ pub mod echobox;
pub mod loader; pub mod loader;
use Tenquestionmarks; use Tenquestionmarks;
use event::Event; use event::Envelope;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::{Sender, Receiver}; use std::sync::mpsc::{Sender, Receiver};
pub trait Module : Sync { pub trait Module : Sync {
fn register (&self, tenquestionmarks: &Tenquestionmarks) {} fn register (&self, _: &Tenquestionmarks) {}
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {} fn run (&self, _: Sender<Envelope>, _: Receiver<Arc<Envelope>>) {}
fn produce_events<'a>(&'a self, sender: Sender<Arc<Event>>) {}
} }

View File

@ -2,13 +2,12 @@ use modules::Module;
use toml::Table; use toml::Table;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::{Sender, Receiver};
use event::Event; use event::{Event, Envelope};
use {Message, Channel, User}; use Message;
use helpers::command::split_command; use helpers::command::split_command;
use pvn::Error;
use pvn::Fighter; use pvn::Fighter;
use pvn::pirates::{Pirate, Pirates}; use pvn::pirates::{Pirate, Pirates};
use pvn::ninjas::{Ninja, Ninjas}; use pvn::ninjas::{Ninja, Ninjas};
@ -16,7 +15,7 @@ use pvn::ninjas::{Ninja, Ninjas};
pub struct PvnModule {} pub struct PvnModule {}
impl PvnModule { impl PvnModule {
pub fn new (configuration: &Table) -> Box<Module> { pub fn new (_: &Table) -> Box<Module> {
Box::new(PvnModule {}) Box::new(PvnModule {})
} }
} }
@ -133,18 +132,10 @@ impl PirateVsNinja {
} }
} }
} }
fn get_pirate (&mut self, name: &str) -> Result<&Pirate, Error> {
self.pirates.get(name)
}
fn get_ninja (&mut self, name: &str) -> Result<&Ninja, Error> {
self.ninjas.get(name)
}
} }
impl Module for PvnModule { impl Module for PvnModule {
fn consume_events (&self, receiver: Receiver<Arc<Event>>) { fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
let mut pvn = PirateVsNinja { let mut pvn = PirateVsNinja {
pirates: Pirates::new(), pirates: Pirates::new(),
ninjas: Ninjas::new() ninjas: Ninjas::new()
@ -152,8 +143,8 @@ impl Module for PvnModule {
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(event) => { Ok(envelope) => {
match *event { match *envelope.event {
Event::Message { ref message } => { Event::Message { ref message } => {
let command = split_command(&message.content); let command = split_command(&message.content);
debug!("Received message... {:?}", &message.content); debug!("Received message... {:?}", &message.content);

View File

@ -2,10 +2,10 @@ use modules::Module;
use toml::Table; use toml::Table;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::{Sender, Receiver};
use helpers::command::split_command; use helpers::command::split_command;
use event::Event; use event::{Event, Envelope};
use rand; use rand;
pub struct RandomModule { pub struct RandomModule {
@ -35,13 +35,13 @@ impl RandomModule {
} }
impl Module for RandomModule { impl Module for RandomModule {
fn consume_events (&self, receiver: Receiver<Arc<Event>>) { fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
loop { loop {
match receiver.recv() { match receiver.recv() {
Ok(event) => { Ok(envelope) => {
match *event { match *envelope.event {
Event::Message { ref message } => { Event::Message { ref message } => {
debug!("Received message... {:?}", message.content); debug!("Received message... {:?}", message.content);
match split_command(&message.content) { match split_command(&message.content) {

View File

@ -6,8 +6,8 @@ use toml::Table;
use {MessageSender, Message, User}; use {MessageSender, Message, User};
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Sender; use std::sync::mpsc::{Sender, Receiver};
use event::Event; use event::{Event, Envelope};
pub struct StdinModule {} pub struct StdinModule {}
@ -23,17 +23,17 @@ impl MessageSender for StdinMessageSender {
} }
impl StdinModule { impl StdinModule {
pub fn new (configuration: &Table) -> Box<Module> { pub fn new (_: &Table) -> Box<Module> {
Box::new(StdinModule {}) Box::new(StdinModule {})
} }
} }
impl Module for StdinModule { impl Module for StdinModule {
fn produce_events<'a>(&'a self, sender: Sender<Arc<Event>>) { fn run(&self, sender: Sender<Envelope>, _: Receiver<Arc<Envelope>>) {
loop { loop {
let mut input = String::new(); let mut input = String::new();
match io::stdin().read_line(&mut input) { match io::stdin().read_line(&mut input) {
Ok(n) => { Ok(_) => {
let message = Message { let message = Message {
author: User { author: User {
name: String::from("Dave"), name: String::from("Dave"),
@ -45,7 +45,7 @@ impl Module for StdinModule {
channel: None channel: None
}; };
match sender.send(Arc::new(Event::Message { message: message })) { match sender.send(Envelope::new(Event::Message { message: message })) {
Err(err) => error!("Error sending message event: {:?}", err), Err(err) => error!("Error sending message event: {:?}", err),
Ok(_) => {} Ok(_) => {}
} }