nativeshell 0.1.0

NativeShell Rust package
Documentation
use std::{cell::RefCell, collections::HashMap, rc::Rc};

use crate::codec::{
    MessageChannel, MessageReply, MessageSender, MethodCall, MethodCallReply, MethodChannel,
    MethodInvoker, StandardMethodCodec, Value,
};

use super::{Context, EngineHandle, EngineManager};

type MessageCallback = dyn Fn(Value, MessageReply<Value>, EngineHandle);
type MethodCallback = dyn Fn(MethodCall<Value>, MethodCallReply<Value>, EngineHandle);

pub struct MessageManager {
    context: Rc<Context>,

    message_channels: HashMap<EngineHandle, HashMap<String, MessageChannel<Value>>>,
    message_handlers: Rc<RefCell<HashMap<String, Box<MessageCallback>>>>,

    method_channels: HashMap<EngineHandle, HashMap<String, MethodChannel<Value>>>,
    method_handlers: Rc<RefCell<HashMap<String, Box<MethodCallback>>>>,
}

impl MessageManager {
    pub(super) fn new(context: Rc<Context>) -> Self {
        Self {
            context,
            message_channels: HashMap::new(),
            message_handlers: Rc::new(RefCell::new(HashMap::new())),
            method_channels: HashMap::new(),
            method_handlers: Rc::new(RefCell::new(HashMap::new())),
        }
    }

    pub fn register_message_handler<F>(&mut self, channel: &str, callback: F)
    where
        F: Fn(Value, MessageReply<Value>, EngineHandle) + 'static,
    {
        let context = self.context.clone();
        if !self
            .message_handlers
            .as_ref()
            .borrow()
            .contains_key(channel)
        {
            // register handlers on engines
            let manager = context.engine_manager.borrow();
            let engines = manager.get_all_engines();
            for engine in engines {
                self.register_message_channel_for_engine(&manager, engine, channel);
            }
        }

        self.message_handlers
            .as_ref()
            .borrow_mut()
            .insert(channel.into(), Box::new(callback));
    }

    pub fn register_method_handler<F>(&mut self, channel: &str, callback: F)
    where
        F: Fn(MethodCall<Value>, MethodCallReply<Value>, EngineHandle) + 'static,
    {
        let context = self.context.clone();
        if !self.method_handlers.as_ref().borrow().contains_key(channel) {
            // register handlers on engines
            let manager = context.engine_manager.borrow();
            let engines = manager.get_all_engines();
            for engine in engines {
                self.register_method_channel_for_engine(&manager, engine, channel);
            }
        }

        self.method_handlers
            .as_ref()
            .borrow_mut()
            .insert(channel.into(), Box::new(callback));
    }

    pub fn unregister_message_handler(&mut self, channel: &str) {
        self.message_handlers.as_ref().borrow_mut().remove(channel);

        for entry in self.message_channels.values_mut() {
            entry.remove(channel);
        }
    }

    pub fn unregister_method_handler(&mut self, channel: &str) {
        self.method_handlers.as_ref().borrow_mut().remove(channel);

        for entry in self.method_channels.values_mut() {
            entry.remove(channel);
        }
    }

    pub fn get_message_sender(
        &self,
        engine: EngineHandle,
        channel: &str,
    ) -> Option<MessageSender<Value>> {
        self.message_channels
            .get(&engine)
            .and_then(|e| e.get(channel))
            .map(|e| e.sender().clone())
    }

    pub fn get_method_invoker(
        &self,
        engine: EngineHandle,
        channel: &str,
    ) -> Option<MethodInvoker<Value>> {
        self.method_channels
            .get(&engine)
            .and_then(|e| e.get(channel))
            .map(|e| e.invoker().clone())
    }

    pub(super) fn engine_created(&mut self, engine_manager: &EngineManager, engine: EngineHandle) {
        let message_keys: Vec<String> = self
            .message_handlers
            .as_ref()
            .borrow()
            .keys()
            .map(|s| s.into())
            .collect();
        for channel in message_keys {
            self.register_message_channel_for_engine(engine_manager, engine, &channel);
        }

        let method_keys: Vec<String> = self
            .method_handlers
            .as_ref()
            .borrow()
            .keys()
            .map(|s| s.into())
            .collect();

        for channel in method_keys {
            self.register_method_channel_for_engine(engine_manager, engine, &channel);
        }
    }

    fn on_message(
        handlers: Rc<RefCell<HashMap<String, Box<MessageCallback>>>>,
        value: Value,
        channel: &str,
        reply: MessageReply<Value>,
        engine: EngineHandle,
    ) {
        let handlers = handlers.as_ref().borrow();
        if let Some(handler) = handlers.get(channel) {
            handler(value, reply, engine);
        }
    }

    fn on_method(
        handlers: Rc<RefCell<HashMap<String, Box<MethodCallback>>>>,
        call: MethodCall<Value>,
        channel: &str,
        reply: MethodCallReply<Value>,
        engine: EngineHandle,
    ) {
        let handlers = handlers.as_ref().borrow();
        if let Some(handler) = handlers.get(channel) {
            handler(call, reply, engine);
        }
    }

    fn register_message_channel_for_engine(
        &mut self,
        engine_manager: &EngineManager,
        engine: EngineHandle,
        channel: &str,
    ) {
        let channel_str = String::from(channel);
        let handlers = self.message_handlers.clone();
        let message_channel = MessageChannel::new_with_engine_manager(
            self.context.clone(),
            engine,
            channel,
            &StandardMethodCodec,
            move |value, reply| {
                Self::on_message(handlers.clone(), value, &channel_str, reply, engine);
            },
            engine_manager,
        );
        let map = self.message_channels.entry(engine);
        let entry = map.or_insert_with(HashMap::new);
        entry.insert(channel.into(), message_channel);
    }

    fn register_method_channel_for_engine(
        &mut self,
        engine_manager: &EngineManager,
        engine: EngineHandle,
        channel: &str,
    ) {
        let channel_str = String::from(channel);
        let handlers = self.method_handlers.clone();
        let method_channel = MethodChannel::new_with_engine_manager(
            self.context.clone(),
            engine,
            channel,
            &StandardMethodCodec,
            move |call, reply| {
                Self::on_method(handlers.clone(), call, &channel_str, reply, engine);
            },
            engine_manager,
        );
        let map = self.method_channels.entry(engine);
        let entry = map.or_insert_with(HashMap::new);
        entry.insert(channel.into(), method_channel);
    }
}