use std::thread::{self, ThreadId};
use crate::{
actor::{ChainContext, SendChainContext, collect_command_contexts},
runner::{TradingCommandMessage, dispatch_scoped_trading_command},
};
#[derive(Debug)]
pub struct DispatchMessage<T> {
message: Option<T>,
context: Option<SendChainContext>,
}
impl<T> DispatchMessage<T> {
#[must_use]
pub fn new(message: T, owner: ThreadId) -> Self {
let context = (owner == thread::current().id())
.then(SendChainContext::capture)
.flatten();
Self {
message: Some(message),
context,
}
}
pub fn dispatch<R>(mut self, run: impl FnOnce(T) -> R) -> R {
let context = self.take_context();
context.with_chain(|| run(self.message.take().expect("message is present")))
}
#[must_use]
pub fn is_rooted(&self) -> bool {
self.context.is_some()
}
fn take_context(&self) -> ChainContext {
collect_command_contexts();
self.context
.as_ref()
.map_or_else(ChainContext::independent, |context| {
context.take().expect("channel context is present")
})
}
}
impl DispatchMessage<TradingCommandMessage> {
pub fn dispatch_trading(mut self, before: impl FnMut(&TradingCommandMessage)) {
let context = self.take_context();
dispatch_scoped_trading_command(
self.message.take().expect("message is present"),
context,
before,
);
}
}
impl<T: std::fmt::Display> std::fmt::Display for DispatchMessage<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.message.as_ref().expect("message is present").fmt(f)
}
}
impl<T> From<T> for DispatchMessage<T> {
fn from(message: T) -> Self {
Self {
message: Some(message),
context: None,
}
}
}
impl<T> Drop for DispatchMessage<T> {
fn drop(&mut self) {
if self.message.is_none() {
return;
}
let context = self
.context
.as_ref()
.filter(|context| context.is_owner())
.and_then(SendChainContext::take)
.unwrap_or_else(ChainContext::independent);
context.with_chain(|| drop(self.message.take()));
}
}