#![allow(dead_code)]
#[cfg(test)]
mod hello_world {
use crate::*;
ghost_chan! {
pub chan HelloWorldActor<GhostError> {
fn hello_world() -> String;
}
}
struct HelloWorldImpl;
impl GhostControlHandler for HelloWorldImpl {}
impl GhostHandler<HelloWorldActor> for HelloWorldImpl {}
impl HelloWorldActorHandler for HelloWorldImpl {
fn handle_hello_world(
&mut self,
) -> HelloWorldActorHandlerResult<String> {
Ok(must_future::MustBoxFuture::new(async move {
Ok("hello world!".to_string())
}))
}
}
impl HelloWorldImpl {
pub async fn spawn() -> GhostSender<HelloWorldActor> {
let builder = actor_builder::GhostActorBuilder::new();
let sender = builder
.channel_factory()
.create_channel::<HelloWorldActor>()
.await
.unwrap();
tokio::task::spawn(builder.spawn(HelloWorldImpl));
sender
}
}
#[tokio::test]
async fn hello_world_example() {
let hello_world = HelloWorldImpl::spawn().await;
assert_eq!("hello world!", &hello_world.hello_world().await.unwrap());
}
}
#[cfg(test)]
mod tests {
use crate::*;
use must_future::*;
#[derive(Debug, thiserror::Error)]
pub enum MyError {
GhostError(#[from] GhostError),
}
pub struct NotDebug;
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
ghost_chan! {
pub chan MyInternalChan<MyError> {
fn test_msg(input: String) -> String;
}
}
ghost_chan! {
pub chan MyActor<MyError> {
fn test_message(input: String) -> String;
fn add_one(input: u32) -> u32;
fn req_not_debug(input: NotDebug) -> ();
fn funky_internal(input: String) -> String;
fn funky_stop() -> ();
}
}
struct MyActorImpl {
internal_sender: GhostSender<MyInternalChan>,
did_shutdown: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl GhostControlHandler for MyActorImpl {
fn handle_ghost_actor_shutdown(
self,
) -> must_future::MustBoxFuture<'static, ()> {
self.did_shutdown
.store(true, std::sync::atomic::Ordering::SeqCst);
must_future::MustBoxFuture::new(async move {})
}
}
impl GhostHandler<MyInternalChan> for MyActorImpl {}
impl MyInternalChanHandler for MyActorImpl {
fn handle_test_msg(
&mut self,
input: String,
) -> MyInternalChanHandlerResult<String> {
Ok(async move { Ok(format!("internal respond to: {}", input)) }
.must_box())
}
}
impl GhostHandler<MyActor> for MyActorImpl {}
impl MyActorHandler for MyActorImpl {
fn handle_test_message(
&mut self,
input: String,
) -> MyActorHandlerResult<String> {
Ok(async move { Ok(format!("echo: {}", input)) }.must_box())
}
fn handle_add_one(&mut self, input: u32) -> MyActorHandlerResult<u32> {
Ok(async move { Ok(input + 1) }.must_box())
}
fn handle_req_not_debug(
&mut self,
_input: NotDebug,
) -> MyActorHandlerResult<()> {
Ok(async move { Ok(()) }.must_box())
}
fn handle_funky_internal(
&mut self,
input: String,
) -> MyActorHandlerResult<String> {
let fut = self.internal_sender.test_msg(input);
Ok(async move { Ok(fut.await.unwrap()) }.must_box())
}
fn handle_funky_stop(&mut self) -> MyActorHandlerResult<()> {
let fut = self.internal_sender.ghost_actor_shutdown_immediate();
Ok(async move { Ok(fut.await.unwrap()) }.must_box())
}
}
impl MyActorImpl {
pub async fn spawn() -> Result<
(
GhostSender<MyActor>,
std::sync::Arc<std::sync::atomic::AtomicBool>,
),
MyError,
> {
let did_shutdown =
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let did_shutdown_clone = did_shutdown.clone();
let builder = actor_builder::GhostActorBuilder::new();
let sender = builder
.channel_factory()
.create_channel::<MyActor>()
.await
.unwrap();
let internal_sender = builder
.channel_factory()
.create_channel::<MyInternalChan>()
.await
.unwrap();
tokio::task::spawn(builder.spawn(MyActorImpl {
internal_sender,
did_shutdown,
}));
Ok((sender, did_shutdown_clone))
}
}
fn init_tracing() {
let _ = tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(
tracing_subscriber::EnvFilter::from_default_env(),
)
.compact()
.finish(),
);
}
#[tokio::test]
async fn it_can_use_eq_on_senders() {
let (sender_a1, _) = MyActorImpl::spawn().await.unwrap();
let (sender_b1, _) = MyActorImpl::spawn().await.unwrap();
let sender_a2 = sender_a1.clone();
assert!(sender_a1 == sender_a2);
assert!(sender_a1 != sender_b1);
}
#[tokio::test]
async fn it_can_hash_senders() {
let (sender_a1, _) = MyActorImpl::spawn().await.unwrap();
let (sender_b1, _) = MyActorImpl::spawn().await.unwrap();
let sender_a2 = sender_a1.clone();
let mut set = std::collections::HashSet::new();
assert!(set.insert(sender_a1));
assert!(set.insert(sender_b1));
assert!(!set.insert(sender_a2));
assert_eq!(2, set.len());
}
#[tokio::test]
async fn it_check_echo() {
init_tracing();
let (sender, _) = MyActorImpl::spawn().await.unwrap();
assert_eq!(
"echo: test",
&sender.test_message("test".to_string()).await.unwrap(),
);
}
#[tokio::test]
async fn it_check_add_1() {
init_tracing();
let (sender, _) = MyActorImpl::spawn().await.unwrap();
assert_eq!(43, sender.add_one(42).await.unwrap());
}
#[tokio::test]
async fn it_check_internal() {
init_tracing();
let (sender, _) = MyActorImpl::spawn().await.unwrap();
assert_eq!(
"internal respond to: i_test",
&sender.funky_internal("i_test".into()).await.unwrap(),
);
}
#[tokio::test]
async fn it_check_shutdown() {
init_tracing();
let (sender, did_shutdown) = MyActorImpl::spawn().await.unwrap();
sender.ghost_actor_shutdown().await.unwrap();
let res = format!("{:?}", sender.add_one(42).await);
assert_eq!("Err(GhostError(Disconnected))", res);
assert!(did_shutdown.load(std::sync::atomic::Ordering::SeqCst));
}
#[tokio::test]
async fn it_check_internal_shutdown() {
init_tracing();
let (sender, did_shutdown) = MyActorImpl::spawn().await.unwrap();
sender.funky_stop().await.unwrap();
let res = format!("{:?}", sender.add_one(42).await);
assert_eq!("Err(GhostError(Disconnected))", res);
assert!(did_shutdown.load(std::sync::atomic::Ordering::SeqCst));
}
}