use crate::component::{ComponentId, Message, State};
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
type MessageQueueMap = Rc<RefCell<HashMap<ComponentId, VecDeque<Box<dyn Message>>>>>;
type TopicMessageQueueMap = Rc<RefCell<HashMap<String, VecDeque<Box<dyn Message>>>>>;
#[derive(Clone)]
pub struct Dispatcher {
queues: MessageQueueMap,
topic_queues: TopicMessageQueueMap,
}
pub struct StateMap {
states: RefCell<HashMap<ComponentId, Box<dyn State>>>,
}
pub struct TopicStore {
states: RefCell<HashMap<String, Box<dyn State>>>,
owners: RefCell<HashMap<String, ComponentId>>,
}
pub struct Context {
pub(crate) current_component_id: ComponentId,
pub(crate) dispatch: Dispatcher,
pub(crate) states: StateMap,
pub(crate) topics: Rc<TopicStore>,
pub(crate) message_queues: MessageQueueMap,
pub(crate) topic_message_queues: TopicMessageQueueMap,
}
impl Dispatcher {
pub fn new(queues: MessageQueueMap, topic_queues: TopicMessageQueueMap) -> Self {
Self {
queues,
topic_queues,
}
}
pub fn send(&self, component_id: ComponentId, message: Box<dyn Message>) {
let mut queues = self.queues.borrow_mut();
queues.entry(component_id).or_default().push_back(message);
}
pub fn send_to_topic(&self, topic: String, message: Box<dyn Message>) {
let mut queues = self.topic_queues.borrow_mut();
queues.entry(topic).or_default().push_back(message);
}
}
impl StateMap {
pub fn new() -> Self {
Self {
states: RefCell::new(HashMap::new()),
}
}
pub fn get_or_init<T: State + Default + Clone + 'static>(
&self,
component_id: &ComponentId,
) -> T {
let mut states = self.states.borrow_mut();
if let Some(existing_state) = states.get(component_id)
&& let Some(typed_state) = existing_state.as_any().downcast_ref::<T>()
{
return typed_state.clone();
}
let new_state = Box::new(T::default());
let cloned = State::as_any(&*new_state)
.downcast_ref::<T>()
.unwrap()
.clone();
states.insert(component_id.clone(), new_state);
cloned
}
pub fn insert(&self, component_id: ComponentId, state: Box<dyn State>) {
self.states.borrow_mut().insert(component_id, state);
}
pub fn remove(&self, component_id: &ComponentId) -> Option<Box<dyn State>> {
self.states.borrow_mut().remove(component_id)
}
}
impl TopicStore {
pub fn new() -> Self {
Self {
states: RefCell::new(HashMap::new()),
owners: RefCell::new(HashMap::new()),
}
}
pub(crate) fn update_topic(
&self,
topic: String,
state: Box<dyn State>,
component_id: ComponentId,
) -> bool {
let mut owners = self.owners.borrow_mut();
let mut states = self.states.borrow_mut();
if let Some(owner) = owners.get(&topic) {
if owner == &component_id {
states.insert(topic, state);
true
} else {
false
}
} else {
owners.insert(topic.clone(), component_id);
states.insert(topic, state);
true
}
}
pub(crate) fn claim_topic(&self, topic: String, component_id: ComponentId) -> bool {
let mut owners = self.owners.borrow_mut();
use std::collections::hash_map::Entry;
if let Entry::Vacant(e) = owners.entry(topic) {
e.insert(component_id);
true
} else {
false
}
}
pub fn read_topic<T: State + Clone + 'static>(&self, topic: &str) -> Option<T> {
let states = self.states.borrow();
states
.get(topic)
.and_then(|state| state.as_any().downcast_ref::<T>().cloned())
}
pub fn get_topic_owner(&self, topic: &str) -> Option<ComponentId> {
self.owners.borrow().get(topic).cloned()
}
pub fn get_owned_topics(&self, component_id: &ComponentId) -> Vec<String> {
self.owners
.borrow()
.iter()
.filter_map(|(topic, owner)| {
if owner == component_id {
Some(topic.clone())
} else {
None
}
})
.collect()
}
}
impl Context {
pub fn new() -> Self {
let queues = Rc::new(RefCell::new(HashMap::new()));
let topic_queues = Rc::new(RefCell::new(HashMap::new()));
Self {
current_component_id: ComponentId::default(),
dispatch: Dispatcher::new(queues.clone(), topic_queues.clone()),
states: StateMap::new(),
topics: Rc::new(TopicStore::new()),
message_queues: queues,
topic_message_queues: topic_queues,
}
}
pub fn handler<T: Message + Clone + 'static>(&self, msg: T) -> impl Fn() + 'static {
let id = self.current_component_id.clone();
let dispatcher = self.dispatch.clone();
move || {
dispatcher.send(id.clone(), Box::new(msg.clone()));
}
}
pub fn handler_with_value<T, F>(&self, msg_fn: F) -> impl Fn(T) + 'static
where
T: 'static,
F: Fn(T) -> Box<dyn Message> + 'static,
{
let id = self.current_component_id.clone();
let dispatcher = self.dispatch.clone();
move |value| {
dispatcher.send(id.clone(), msg_fn(value));
}
}
pub fn get_state<T: State + Default + Clone + 'static>(&self) -> T {
self.states.get_or_init::<T>(&self.current_component_id)
}
pub fn set_state(&self, state: Box<dyn State>) {
self.states.insert(self.current_component_id.clone(), state);
}
pub fn read_topic<T: State + Clone + 'static>(&self, topic: &str) -> Option<T> {
self.topics.read_topic(topic)
}
pub fn send_to_topic(&self, topic: impl Into<String>, message: Box<dyn Message>) {
self.dispatch.send_to_topic(topic.into(), message);
}
pub fn topic_handler<T: Message + Clone + 'static>(
&self,
topic: impl Into<String>,
msg: T,
) -> impl Fn() + 'static {
let topic = topic.into();
let dispatcher = self.dispatch.clone();
move || {
dispatcher.send_to_topic(topic.clone(), Box::new(msg.clone()));
}
}
pub fn topic_handler_with_value<T, F>(
&self,
topic: impl Into<String>,
msg_fn: F,
) -> impl Fn(T) + 'static
where
T: 'static,
F: Fn(T) -> Box<dyn Message> + 'static,
{
let topic = topic.into();
let dispatcher = self.dispatch.clone();
move |value| {
dispatcher.send_to_topic(topic.clone(), msg_fn(value));
}
}
pub fn child(&self, index: usize) -> Self {
Self {
current_component_id: self.current_component_id.child(index),
dispatch: self.dispatch.clone(),
states: StateMap::new(), topics: self.topics.clone(), message_queues: self.message_queues.clone(), topic_message_queues: self.topic_message_queues.clone(), }
}
pub fn drain_messages(&self, component_id: &ComponentId) -> Vec<Box<dyn Message>> {
let mut queues = self.message_queues.borrow_mut();
if let Some(queue) = queues.get_mut(component_id) {
queue.drain(..).collect()
} else {
Vec::new()
}
}
pub fn drain_topic_messages(&self, topic: &str) -> Vec<Box<dyn Message>> {
let mut queues = self.topic_message_queues.borrow_mut();
if let Some(queue) = queues.get_mut(topic) {
queue.drain(..).collect()
} else {
Vec::new()
}
}
pub fn drain_all_messages(&self) -> Vec<(Box<dyn Message>, Option<String>)> {
let mut all_messages = Vec::new();
for msg in self.drain_messages(&self.current_component_id) {
all_messages.push((msg, None));
}
let owned_topics = self.topics.get_owned_topics(&self.current_component_id);
for topic in owned_topics {
for msg in self.drain_topic_messages(&topic) {
all_messages.push((msg, Some(topic.clone())));
}
}
let unassigned = self.get_unassigned_topic_messages();
for (topic, msg) in unassigned {
all_messages.push((msg, Some(topic)));
}
all_messages
}
fn get_unassigned_topic_messages(&self) -> Vec<(String, Box<dyn Message>)> {
let mut unassigned = Vec::new();
let topic_queues = self.topic_message_queues.borrow();
for (topic, queue) in topic_queues.iter() {
if self.topics.get_topic_owner(topic).is_none() && !queue.is_empty() {
for msg in queue.iter() {
unassigned.push((topic.clone(), msg.clone_box()));
}
}
}
unassigned
}
pub fn drain_topic_if_claimed(&self, topic: &str, component_id: &ComponentId) {
if let Some(owner) = self.topics.get_topic_owner(topic)
&& owner == *component_id
{
let mut queues = self.topic_message_queues.borrow_mut();
if let Some(queue) = queues.get_mut(topic) {
queue.clear();
}
}
}
}
impl Default for TopicStore {
fn default() -> Self {
Self::new()
}
}
impl Default for StateMap {
fn default() -> Self {
Self::new()
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}