From fcc86a671e58b677c984f1ebd19c8a1ed509446d Mon Sep 17 00:00:00 2001 From: Adrian Malacoda Date: Thu, 22 Feb 2018 01:09:39 -0600 Subject: [PATCH] rearchitect event transmission so that the parents/children of each module are explicitly specified and we establish the linkages between them --- Cargo.toml | 1 + src/event/mod.rs | 7 +---- src/lib.rs | 72 +++++++++++++++++++++---------------------- src/modules/mod.rs | 27 ++++++++++++++-- tenquestionmarks.toml | 10 ++++++ 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3982e17..bc8a493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ env_logger = "0.5.3" transformable_channels = "0.1.1" time = "0.1" regex = "0.2" +multimap = "0.4.0" pvn = { git = "http://gitlab.monarch-pass.net/malacoda/pvn.git" } echobox = { git = "http://gitlab.monarch-pass.net/malacoda/echobox.git" } stc = { git = "http://gitlab.monarch-pass.net/malacoda/stc.git" } diff --git a/src/event/mod.rs b/src/event/mod.rs index e166867..6e05d65 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,8 +1,5 @@ use {Message, Channel, User}; -use std::sync::Arc; -use std::collections::btree_set::BTreeSet; - #[derive(Debug)] pub enum Event { Message { message: Message }, // A user sends a message @@ -18,15 +15,13 @@ pub enum Event { pub struct Envelope { pub from: Option, pub event: Event, - pub to: Vec } impl Envelope { pub fn new (event: Event) -> Envelope { Envelope { from: None, - event: event, - to: vec![] + event: event } } } diff --git a/src/lib.rs b/src/lib.rs index eef2532..f623477 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ extern crate echobox; extern crate transformable_channels; extern crate stc; extern crate regex; +extern crate multimap; #[macro_use] extern crate hlua; @@ -27,6 +28,7 @@ use std::sync::mpsc; use std::sync::mpsc::Sender; use transformable_channels::mpsc::TransformableSender; +use multimap::MultiMap; mod helpers; @@ -34,18 +36,31 @@ mod helpers; extern crate log; pub struct Tenquestionmarks { - subscriptions: BTreeMap + modules: BTreeMap, + subscriptions: MultiMap } impl Tenquestionmarks { pub fn with_modules (modules: BTreeMap) -> Tenquestionmarks { - let tqm = Tenquestionmarks { - subscriptions: modules.into_iter().map(|(key, module)| { - (key.clone(), Subscription::new(key.clone(), module)) - }).collect() - }; + let mut subscriptions = MultiMap::new(); + for (name, module) in modules.iter() { + for parent in module.parents() { + info!("{:?} registered as parent of {:?}", parent, name); + subscriptions.insert(parent, Subscription::new(name.to_owned(), &module)); + } - tqm + for child_name in module.children() { + if let Some(ref child) = modules.get(&child_name) { + info!("{:?} registered as child of {:?}", child_name, name); + subscriptions.insert(name.clone(), Subscription::new(child_name.to_owned(), &child)); + } + } + } + + Tenquestionmarks { + modules: modules, + subscriptions: subscriptions + } } pub fn from_configuration (configuration: Table) -> Result { @@ -65,20 +80,19 @@ impl Tenquestionmarks { // into all other Modules. // tenquestionmarks propagates all events to each Module through these // channels. - let module_senders: BTreeMap<&str, Sender>> = self.subscriptions.iter().map(|(key, subscription)| { + let module_senders: BTreeMap<&str, Sender>> = self.modules.iter().map(|(key, module)| { let from = key.clone(); let main_sender_mapped = main_sender.map(move |envelope: Envelope| { Envelope { from: Some(from.clone()), - event: envelope.event, - to: envelope.to + event: envelope.event } }); let (module_sender, module_receiver) = mpsc::channel(); info!("Spawning thread for \"{}\"", key); scope.spawn(move || { - subscription.module.run(Box::new(main_sender_mapped), module_receiver); + module.run(Box::new(main_sender_mapped), module_receiver); info!("Thread for \"{}\" is exiting", key); }); (&key[..], module_sender) @@ -98,16 +112,17 @@ impl Tenquestionmarks { * names the target module. */ let arc_envelope = Arc::new(envelope); - for (key, sender) in &module_senders { - let from_this = String::from(*key); - if let Some(subscription) = self.subscriptions.get::(&from_this) { - if subscription.can_handle_event(&arc_envelope) { - if let Err(err) = sender.send(arc_envelope.clone()) { - debug!("Failed to dispatch event to module \"{}\": {:?}", key, err); + if let Some(ref from) = arc_envelope.from { + if let Some(subscriptions) = self.subscriptions.get_vec(&**from) { + for subscription in subscriptions { + if subscription.can_handle_event(&arc_envelope) { + if let Some(sender) = module_senders.get(&*subscription.name) { + if let Err(err) = sender.send(arc_envelope.clone()) { + debug!("Failed to dispatch event to module \"{}\": {:?}", subscription.name, err); + } + } } } - } else { - debug!("Failed to dispatch event to module \"{}\": No such module found!", key); } } }, @@ -169,13 +184,12 @@ trait EventFilter: Sync + Send { } struct Subscription { - pub module: Module, pub name: String, pub filters: Vec> } impl Subscription { - pub fn new (name: String, module: Module) -> Subscription { + pub fn new (name: String, module: &Module) -> Subscription { let filters: Vec> = module.config.get("filters") .and_then(|value| value.as_array()) .map(|value| value.to_vec()) @@ -192,27 +206,13 @@ impl Subscription { .collect(); Subscription { - module: module, name: name, filters: filters } } pub fn can_handle_event (&self, envelope: &Envelope) -> bool { - if Some(&self.name) == envelope.from.as_ref() { - debug!("Refusing to transmit event to its originator ({:?})", self.name); - return false; - } - else if !(envelope.to.is_empty() || !envelope.to.contains(&self.name)) { - debug!( - "Refusing to transmit envelope from {:?} to {:?} since it is not on the list of allowed recipients ({:?})", - envelope.from, - self.name, - envelope.to - ); - return false; - } - else if !self.filters.is_empty() { + if !self.filters.is_empty() { for filter in &self.filters { if filter.accept(envelope) { return true; diff --git a/src/modules/mod.rs b/src/modules/mod.rs index d5e7e20..4cbd0dc 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -10,7 +10,6 @@ pub mod logger; pub mod loader; -use Tenquestionmarks; use event::Envelope; use std::sync::Arc; @@ -22,13 +21,37 @@ use toml::value::Table; pub struct Module { event_loop: Box, module_type: String, - pub config: Table + pub config: Table, } impl Module { pub fn run (&self, sender: Box>, receiver: Receiver>) { self.event_loop.run(sender, receiver); } + + pub fn parents (&self) -> Vec { + self.config.get("parents") + .and_then(|value| value.as_array()) + .map(|value| value.to_vec()) + .unwrap_or(vec![]) + .iter() + .map(|value| value.as_str()) + .filter(|value| value.is_some()) + .map(|value| value.unwrap().to_owned()) + .collect() + } + + pub fn children (&self) -> Vec { + self.config.get("children") + .and_then(|value| value.as_array()) + .map(|value| value.to_vec()) + .unwrap_or(vec![]) + .iter() + .map(|value| value.as_str()) + .filter(|value| value.is_some()) + .map(|value| value.unwrap().to_owned()) + .collect() + } } pub trait EventLoop : Sync { diff --git a/tenquestionmarks.toml b/tenquestionmarks.toml index f1e026c..818a9d4 100644 --- a/tenquestionmarks.toml +++ b/tenquestionmarks.toml @@ -7,10 +7,12 @@ token = "your token here" [stdin] [echo] +parents = ["stdin", "discord"] prefix = "?echo" [no] type = "random" +parents = ["stdin", "discord"] prefix = "?no" responses = [ "https://www.youtube.com/watch?v=WWaLxFIVX1s", # Darth Vader @@ -36,6 +38,7 @@ responses = [ [yes] type = "random" +parents = ["stdin", "discord"] prefix = "?yes" responses = [ "https://www.youtube.com/watch?v=JPVaDaynNKM", # Captain Falcon @@ -58,14 +61,18 @@ responses = [ [chk] type = "random" +parents = ["stdin", "discord"] prefix = "?chk" responses = ["ack"] [pvn] +parents = ["stdin", "discord"] [echobox] +parents = ["stdin", "discord"] [lua] +parents = ["stdin", "discord"] code = """ function on_message (message, reply) reply("Lua says: " .. message) @@ -75,6 +82,7 @@ foo = "bar" [lua2] type = "lua" +parents = ["stdin", "discord"] filters = [{ username = "David" }] code = """ function on_message (message, reply) @@ -83,6 +91,8 @@ end """ [autolink] +parents = ["stdin", "discord"] [logger] +parents = ["stdin", "discord"] filters = [{ username = "Dave" }, { username = "Kevin" }]