rearchitect event transmission so that the parents/children of each module are explicitly specified and we establish the linkages between them

This commit is contained in:
Adrian Malacoda 2018-02-22 01:09:39 -06:00
parent 3614c7eb5d
commit fcc86a671e
5 changed files with 73 additions and 44 deletions

View File

@ -14,6 +14,7 @@ env_logger = "0.5.3"
transformable_channels = "0.1.1" transformable_channels = "0.1.1"
time = "0.1" time = "0.1"
regex = "0.2" regex = "0.2"
multimap = "0.4.0"
pvn = { git = "http://gitlab.monarch-pass.net/malacoda/pvn.git" } pvn = { git = "http://gitlab.monarch-pass.net/malacoda/pvn.git" }
echobox = { git = "http://gitlab.monarch-pass.net/malacoda/echobox.git" } echobox = { git = "http://gitlab.monarch-pass.net/malacoda/echobox.git" }
stc = { git = "http://gitlab.monarch-pass.net/malacoda/stc.git" } stc = { git = "http://gitlab.monarch-pass.net/malacoda/stc.git" }

View File

@ -1,8 +1,5 @@
use {Message, Channel, User}; use {Message, Channel, User};
use std::sync::Arc;
use std::collections::btree_set::BTreeSet;
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
Message { message: Message }, // A user sends a message Message { message: Message }, // A user sends a message
@ -18,15 +15,13 @@ pub enum Event {
pub struct Envelope { pub struct Envelope {
pub from: Option<String>, pub from: Option<String>,
pub event: Event, pub event: Event,
pub to: Vec<String>
} }
impl Envelope { impl Envelope {
pub fn new (event: Event) -> Envelope { pub fn new (event: Event) -> Envelope {
Envelope { Envelope {
from: None, from: None,
event: event, event: event
to: vec![]
} }
} }
} }

View File

@ -7,6 +7,7 @@ extern crate echobox;
extern crate transformable_channels; extern crate transformable_channels;
extern crate stc; extern crate stc;
extern crate regex; extern crate regex;
extern crate multimap;
#[macro_use] #[macro_use]
extern crate hlua; extern crate hlua;
@ -27,6 +28,7 @@ use std::sync::mpsc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use transformable_channels::mpsc::TransformableSender; use transformable_channels::mpsc::TransformableSender;
use multimap::MultiMap;
mod helpers; mod helpers;
@ -34,18 +36,31 @@ mod helpers;
extern crate log; extern crate log;
pub struct Tenquestionmarks { pub struct Tenquestionmarks {
subscriptions: BTreeMap<String, Subscription> modules: BTreeMap<String, Module>,
subscriptions: MultiMap<String, Subscription>
} }
impl Tenquestionmarks { impl Tenquestionmarks {
pub fn with_modules (modules: BTreeMap<String, Module>) -> Tenquestionmarks { pub fn with_modules (modules: BTreeMap<String, Module>) -> Tenquestionmarks {
let tqm = Tenquestionmarks { let mut subscriptions = MultiMap::new();
subscriptions: modules.into_iter().map(|(key, module)| { for (name, module) in modules.iter() {
(key.clone(), Subscription::new(key.clone(), module)) for parent in module.parents() {
}).collect() 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<Tenquestionmarks, ModuleLoaderError> { pub fn from_configuration (configuration: Table) -> Result<Tenquestionmarks, ModuleLoaderError> {
@ -65,20 +80,19 @@ impl Tenquestionmarks {
// into all other Modules. // 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 module_senders: BTreeMap<&str, Sender<Arc<Envelope>>> = self.subscriptions.iter().map(|(key, subscription)| { let module_senders: BTreeMap<&str, Sender<Arc<Envelope>>> = self.modules.iter().map(|(key, module)| {
let from = key.clone(); let from = key.clone();
let main_sender_mapped = main_sender.map(move |envelope: Envelope| { let main_sender_mapped = main_sender.map(move |envelope: Envelope| {
Envelope { Envelope {
from: Some(from.clone()), from: Some(from.clone()),
event: envelope.event, event: envelope.event
to: envelope.to
} }
}); });
let (module_sender, module_receiver) = mpsc::channel(); let (module_sender, module_receiver) = mpsc::channel();
info!("Spawning thread for \"{}\"", key); info!("Spawning thread for \"{}\"", key);
scope.spawn(move || { 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); info!("Thread for \"{}\" is exiting", key);
}); });
(&key[..], module_sender) (&key[..], module_sender)
@ -98,16 +112,17 @@ impl Tenquestionmarks {
* names the target module. * names the target module.
*/ */
let arc_envelope = Arc::new(envelope); let arc_envelope = Arc::new(envelope);
for (key, sender) in &module_senders { if let Some(ref from) = arc_envelope.from {
let from_this = String::from(*key); if let Some(subscriptions) = self.subscriptions.get_vec(&**from) {
if let Some(subscription) = self.subscriptions.get::<String>(&from_this) { for subscription in subscriptions {
if subscription.can_handle_event(&arc_envelope) { 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()) { if let Err(err) = sender.send(arc_envelope.clone()) {
debug!("Failed to dispatch event to module \"{}\": {:?}", key, err); 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 { struct Subscription {
pub module: Module,
pub name: String, pub name: String,
pub filters: Vec<Box<EventFilter>> pub filters: Vec<Box<EventFilter>>
} }
impl Subscription { impl Subscription {
pub fn new (name: String, module: Module) -> Subscription { pub fn new (name: String, module: &Module) -> Subscription {
let filters: Vec<Box<EventFilter>> = module.config.get("filters") let filters: Vec<Box<EventFilter>> = module.config.get("filters")
.and_then(|value| value.as_array()) .and_then(|value| value.as_array())
.map(|value| value.to_vec()) .map(|value| value.to_vec())
@ -192,27 +206,13 @@ impl Subscription {
.collect(); .collect();
Subscription { Subscription {
module: module,
name: name, name: name,
filters: filters filters: filters
} }
} }
pub fn can_handle_event (&self, envelope: &Envelope) -> bool { pub fn can_handle_event (&self, envelope: &Envelope) -> bool {
if Some(&self.name) == envelope.from.as_ref() { if !self.filters.is_empty() {
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() {
for filter in &self.filters { for filter in &self.filters {
if filter.accept(envelope) { if filter.accept(envelope) {
return true; return true;

View File

@ -10,7 +10,6 @@ pub mod logger;
pub mod loader; pub mod loader;
use Tenquestionmarks;
use event::Envelope; use event::Envelope;
use std::sync::Arc; use std::sync::Arc;
@ -22,13 +21,37 @@ use toml::value::Table;
pub struct Module { pub struct Module {
event_loop: Box<EventLoop>, event_loop: Box<EventLoop>,
module_type: String, module_type: String,
pub config: Table pub config: Table,
} }
impl Module { impl Module {
pub fn run (&self, sender: Box<ExtSender<Envelope>>, receiver: Receiver<Arc<Envelope>>) { pub fn run (&self, sender: Box<ExtSender<Envelope>>, receiver: Receiver<Arc<Envelope>>) {
self.event_loop.run(sender, receiver); self.event_loop.run(sender, receiver);
} }
pub fn parents (&self) -> Vec<String> {
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<String> {
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 { pub trait EventLoop : Sync {

View File

@ -7,10 +7,12 @@ token = "your token here"
[stdin] [stdin]
[echo] [echo]
parents = ["stdin", "discord"]
prefix = "?echo" prefix = "?echo"
[no] [no]
type = "random" type = "random"
parents = ["stdin", "discord"]
prefix = "?no" prefix = "?no"
responses = [ responses = [
"https://www.youtube.com/watch?v=WWaLxFIVX1s", # Darth Vader "https://www.youtube.com/watch?v=WWaLxFIVX1s", # Darth Vader
@ -36,6 +38,7 @@ responses = [
[yes] [yes]
type = "random" type = "random"
parents = ["stdin", "discord"]
prefix = "?yes" prefix = "?yes"
responses = [ responses = [
"https://www.youtube.com/watch?v=JPVaDaynNKM", # Captain Falcon "https://www.youtube.com/watch?v=JPVaDaynNKM", # Captain Falcon
@ -58,14 +61,18 @@ responses = [
[chk] [chk]
type = "random" type = "random"
parents = ["stdin", "discord"]
prefix = "?chk" prefix = "?chk"
responses = ["ack"] responses = ["ack"]
[pvn] [pvn]
parents = ["stdin", "discord"]
[echobox] [echobox]
parents = ["stdin", "discord"]
[lua] [lua]
parents = ["stdin", "discord"]
code = """ code = """
function on_message (message, reply) function on_message (message, reply)
reply("Lua says: " .. message) reply("Lua says: " .. message)
@ -75,6 +82,7 @@ foo = "bar"
[lua2] [lua2]
type = "lua" type = "lua"
parents = ["stdin", "discord"]
filters = [{ username = "David" }] filters = [{ username = "David" }]
code = """ code = """
function on_message (message, reply) function on_message (message, reply)
@ -83,6 +91,8 @@ end
""" """
[autolink] [autolink]
parents = ["stdin", "discord"]
[logger] [logger]
parents = ["stdin", "discord"]
filters = [{ username = "Dave" }, { username = "Kevin" }] filters = [{ username = "Dave" }, { username = "Kevin" }]