use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::iter::IntoIterator;
use failure::Error;
use futures::sync::mpsc::{UnboundedSender, UnboundedReceiver};
use futures::sync::oneshot::Sender as OneshotSender;
use mopa::{Any, mopafy};
use rmpv::Value;
use crate::CloseCode;
pub type BoxedTask = Box<dyn Task + Send>;
pub trait Task : Debug + Any {
fn init(&mut self, data: &Option<HashMap<String, Value>>) -> Result<(), Error>;
fn start(&mut self,
outgoing_tx: UnboundedSender<TaskMessage>,
incoming_rx: UnboundedReceiver<TaskMessage>,
disconnect_tx: OneshotSender<Option<CloseCode>>);
fn supported_types(&self) -> &'static [&'static str];
fn send_signaling_message(&self, payload: &[u8]);
fn name(&self) -> Cow<'static, str>;
fn data(&self) -> Option<HashMap<String, Value>>;
fn close(&mut self, reason: CloseCode);
}
mopafy!(Task);
#[derive(Debug)]
pub(crate) struct Tasks(pub(crate) Vec<BoxedTask>);
impl Tasks {
#[allow(dead_code)]
pub(crate) fn new(task: BoxedTask) -> Self {
Tasks(vec![task])
}
pub(crate) fn from_vec(tasks: Vec<BoxedTask>) -> Result<Tasks, &'static str> {
if tasks.is_empty() {
return Err("Tasks vector may not be empty");
}
Ok(Tasks(tasks))
}
#[allow(dead_code)]
pub(crate) fn add_task(&mut self, task: BoxedTask) -> Result<&mut Self, String> {
if self.0.iter().any(|t| t.name() == task.name()) {
return Err(format!("Task with name \"{}\" cannot be added twice", task.name()));
}
self.0.push(task);
Ok(self)
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
self.0.len()
}
pub(crate) fn choose_shared_task<S: AsRef<str>>(self, tasks: &[S]) -> Option<BoxedTask> {
for task in self.0 {
if tasks.iter().any(|p| p.as_ref() == &*task.name()) {
return Some(task);
}
}
None
}
}
impl IntoIterator for Tasks {
type Item = BoxedTask;
type IntoIter = ::std::vec::IntoIter<BoxedTask>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TaskMessage {
Value(HashMap<String, Value>),
Application(Value),
Close(CloseCode),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::DummyTask;
#[test]
fn create_tasks() {
let t1 = Box::new(DummyTask::new(1));
let t2 = Box::new(DummyTask::new(2));
let t3 = Box::new(DummyTask::new(3));
let mut tasks = Tasks::new(t1);
assert_eq!(tasks.len(), 1);
tasks.add_task(t2).unwrap();
tasks.add_task(t3.clone()).unwrap();
assert_eq!(tasks.len(), 3);
let err = tasks.add_task(t3).unwrap_err();
assert_eq!(err, "Task with name \"dummy.3\" cannot be added twice".to_string());
assert_eq!(tasks.len(), 3);
}
#[test]
fn choose_shared_task() {
fn make_tasks() -> Tasks {
let t1 = Box::new(DummyTask::new(1));
let t2 = Box::new(DummyTask::new(2));
Tasks::from_vec(vec![t1, t2]).unwrap()
};
let chosen = make_tasks().choose_shared_task(&["dummy.1", "dummy.3"]).expect("No shared task found (1)");
assert_eq!(chosen.name(), "dummy.1");
let chosen = make_tasks().choose_shared_task(&vec!["dummy.2".to_string()]).expect("No shared task found (2)");
assert_eq!(chosen.name(), "dummy.2");
let chosen = make_tasks().choose_shared_task(&vec!["dummy.3".to_string()]);
assert!(chosen.is_none());
let chosen = make_tasks().choose_shared_task(&["dummy.2", "dummy.1"]).expect("No shared task found (3)");
assert_eq!(chosen.name(), "dummy.1");
}
}