use std::sync::Arc;
use std::collections::HashMap;
use parking_lot::RwLock;
use control::{Subscription, Ready, Data, Session, Handler, ResponseHandler};
use error::{Error, ErrorCode};
use params::Params;
use response::SubscriptionOutput;
pub enum Method {
Call(Box<MethodCommand>),
Notify(Box<NotificationCommand>),
Subscribe(Arc<Box<SubscriptionCommand>>, String),
Unsubscribe(Arc<Box<SubscriptionCommand>>),
}
pub trait SyncMethodCommand: Send + Sync {
fn execute(&self, params: Params) -> Data;
}
impl<F> SyncMethodCommand for F where F: Fn(Params) -> Data + Sync + Send {
fn execute(&self, params: Params) -> Data {
self(params)
}
}
pub struct SyncMethod<C> {
pub command: C,
}
impl<C: SyncMethodCommand> MethodCommand for SyncMethod<C> {
fn execute(&self, params: Params, ready: Ready) {
ready.ready(self.command.execute(params));
}
}
pub trait MethodCommand: Send + Sync {
fn execute(&self, params: Params, ready: Ready);
}
impl<F> MethodCommand for F where F: Fn(Params, Ready) + Sync + Send {
fn execute(&self, params: Params, ready: Ready) {
self(params, ready)
}
}
pub trait NotificationCommand: Send + Sync {
fn execute(&self, params: Params);
}
impl<F> NotificationCommand for F where F: Fn(Params) + Sync + Send {
fn execute(&self, params: Params) {
self(params)
}
}
pub trait SubscriptionCommand: Send + Sync {
fn execute(&self, subscription: Subscription);
}
impl<F> SubscriptionCommand for F where F: Fn(Subscription) + Sync + Send {
fn execute(&self, subscription: Subscription) {
self(subscription)
}
}
pub struct Commander {
methods: RwLock<HashMap<String, Method>>,
}
impl Commander {
pub fn new() -> Self {
Commander {
methods: RwLock::new(HashMap::new()),
}
}
pub fn add_method<C>(&self, name: String, command: C) where C: MethodCommand + 'static {
self.methods.write().insert(name, Method::Call(Box::new(command)));
}
pub fn add_notification<C>(&self, name: String, command: C) where C: NotificationCommand + 'static {
self.methods.write().insert(name, Method::Notify(Box::new(command)));
}
pub fn add_subscription<C>(&self, subscribe: String, subscription: String, unsubscribe: String, command: C) where C: SubscriptionCommand + 'static {
let command = Arc::new(Box::new(command) as Box<SubscriptionCommand>);
let mut methods = self.methods.write();
methods.insert(subscribe, Method::Subscribe(command.clone(), subscription));
methods.insert(unsubscribe, Method::Unsubscribe(command));
}
pub fn add_methods(&self, methods: HashMap<String, Box<MethodCommand>>) {
let methods: HashMap<_, _> = methods.into_iter().map(|(name, v)| (name, Method::Call(v))).collect();
self.methods.write().extend(methods);
}
pub fn add_notifications(&self, notifications: HashMap<String, Box<NotificationCommand>>) {
let notifications: HashMap<_, _> = notifications.into_iter().map(|(name, v)| (name, Method::Notify(v))).collect();
self.methods.write().extend(notifications);
}
pub fn add_subscriptions(&self, subscriptions: HashMap<(String, String, String), Box<SubscriptionCommand>>) {
let mut methods = self.methods.write();
for ((subscribe, subscription, unsubscribe), command) in subscriptions.into_iter() {
let command = Arc::new(command);
methods.insert(subscribe, Method::Subscribe(command.clone(), subscription));
methods.insert(unsubscribe, Method::Unsubscribe(command));
}
}
pub fn execute_method<A: 'static>(&self, name: String, params: Params, handler: Handler<A, Data, SubscriptionOutput>, session: Option<Session>) {
match (self.methods.read().get(&name), session) {
(Some(&Method::Call(ref command)), _) => {
command.execute(params, handler.into());
},
(Some(&Method::Subscribe(ref subscribe, ref subscription_name)), Some(ref session)) => {
subscribe.execute(Subscription::Open {
params: params,
subscriber: handler.into_subscriber(session.clone(), subscription_name.clone(), subscribe.clone()),
});
},
(Some(&Method::Unsubscribe(ref unsubscribe)), Some(ref session)) => {
if let Params::Array(params) = params {
if let Some(id) = params.into_iter().next() {
session.remove_subscription(name, id.clone());
unsubscribe.execute(Subscription::Close {
id: id,
ready: handler.into(),
});
return;
}
}
handler.send(Err(Error::new(ErrorCode::InvalidParams)))
},
(Some(&Method::Subscribe(_, _)), None) | (Some(&Method::Unsubscribe(_)), None) => {
handler.send(Err(Error::new(ErrorCode::SessionNotSupported)))
},
_ => handler.send(Err(Error::new(ErrorCode::MethodNotFound))),
};
}
pub fn execute_notification(&self, name: String, params: Params) {
if let Some(&Method::Notify(ref command)) = self.methods.read().get(&name) {
command.execute(params)
}
}
}