xasync-macro 0.1.10

Procedural Macros for Cyberex Async
Documentation
#![allow(clippy::enum_variant_names)]
use cyberex::ActorMessage;
use cyberex::xasync::call::ActorPacket;
use static_assertions::assert_impl_all;
use std::any::type_name_of_val;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
#[derive(ActorMessage)]
enum GetTypeNameCommand {
    GetStringTypeName(ActorPacket<String, String>),
    GetI32TypeName(ActorPacket<i32, String>),
    Timeout(ActorPacket<(), ()>),
    ThisError(ActorPacket<(), ()>),
    Event(String),
}

#[derive(thiserror::Error, Debug, PartialEq)]
#[error("Custom error: {message}")]
struct CustomError {
    message: String,
}
#[derive(ActorMessage)]
struct GetVoid(ActorPacket<(), ()>);

#[derive(ActorMessage)]
struct Event(String);

#[tokio::test]
async fn test_case() {
    let (caller, mut replyer) = GetTypeNameCommand::actor();
    assert_impl_all!(GetTypeNameCommandCaller: std::fmt::Debug, Clone);
    assert_impl_all!(GetTypeNameCommandWeakCaller: std::fmt::Debug, Clone);

    assert_eq!(type_name_of_val(&caller), "drive_actor_tests::GetTypeNameCommandCaller");
    assert_eq!(
        type_name_of_val(&caller.downgrade()),
        "drive_actor_tests::GetTypeNameCommandWeakCaller"
    );
    assert_eq!(
        type_name_of_val(&replyer),
        "drive_actor_tests::GetTypeNameCommandReplyer"
    );
    let event_notified = Arc::new(Notify::new());
    tokio::spawn({
        let event_notified = event_notified.clone();
        async move {
            while let Some(command) = replyer.recv().await {
                match command {
                    GetTypeNameCommand::GetI32TypeName(packet) => {
                        let _ = packet.return_(Ok("i32".to_string()));
                    },
                    GetTypeNameCommand::GetStringTypeName(packet) => {
                        let _ = packet.return_ok("string".to_string());
                    },
                    GetTypeNameCommand::Timeout(packet) => {
                        tokio::spawn(async move {
                            tokio::time::sleep(Duration::from_secs(10)).await;
                            let _ = packet.return_ok(());
                        });
                    },
                    GetTypeNameCommand::ThisError(packet) => {
                        let custom_err = CustomError {
                            message: "test error".to_string(),
                        };
                        let _ = packet.return_fail(anyhow::anyhow!(custom_err));
                    },
                    GetTypeNameCommand::Event(event_str) => {
                        assert_eq!(event_str, "Event_str");
                        event_notified.notify_one();
                    },
                };
            }
        }
    });

    {
        let result_str = caller.get_string_type_name("hello".to_string()).await.unwrap();
        assert_eq!(result_str, "string");
    }
    {
        let weak_caller = caller.downgrade();
        let upgraded_caller = weak_caller.upgrade().unwrap();
        let result_str = upgraded_caller.get_i32_type_name(1).await.unwrap();
        assert_eq!(result_str, "i32");
    }
    {
        let result_str = caller.get_i32_type_name(1).await.unwrap();
        assert_eq!(result_str, "i32");
    }
    {
        let result_str = caller.timeout_timeout((), Duration::from_secs(1)).await.unwrap_err();
        assert_eq!(
            result_str.to_string(),
            r#"Fail to `timeout`, error: Receive result fail, timeout: 1s"#
        );
    }
    {
        // custom error can be keep
        let result = caller.this_error(()).await;
        assert!(result.is_err());

        let err = result.unwrap_err();
        assert_eq!(
            format!("{}", err),
            "Fail to `this_error`, error: Custom error: test error"
        );

        let custom_error = err.downcast_ref::<CustomError>();
        assert!(custom_error.is_some());
        assert_eq!(custom_error.unwrap().message, "test error");
    }
    {
        caller.event("Event_str".to_string());
        event_notified.notified().await;
    }
}

#[tokio::test]
async fn test_single_struct_case() {
    let (caller, mut replyer) = GetVoid::actor();
    assert_impl_all!(GetVoidCaller: std::fmt::Debug, Clone);
    assert_impl_all!(GetVoidWeakCaller: std::fmt::Debug, Clone);

    assert_eq!(type_name_of_val(&caller), "drive_actor_tests::GetVoidCaller");
    assert_eq!(
        type_name_of_val(&caller.downgrade()),
        "drive_actor_tests::GetVoidWeakCaller"
    );
    assert_eq!(type_name_of_val(&replyer), "drive_actor_tests::GetVoidReplyer");

    tokio::spawn(async move {
        while let Some(command) = replyer.recv().await {
            let _ = command.0.return_ok(());
        }
    });

    {
        let weak_caller = caller.downgrade();
        let upgraded_caller = weak_caller.upgrade().unwrap();
        upgraded_caller.get_void(()).await.unwrap();
    }

    caller.get_void(()).await.unwrap();
}

#[tokio::test]
async fn test_single_struct_weak_upgrade_none_after_drop() {
    let weak_caller = {
        let (void_caller, _void_replyer) = GetVoid::actor();
        void_caller.downgrade()
    };

    assert!(weak_caller.upgrade().is_none());
}

#[tokio::test]
async fn test_event_struct_case() {
    let (caller, mut replyer) = Event::actor();
    assert_impl_all!(EventCaller: std::fmt::Debug, Clone);
    assert_impl_all!(EventWeakCaller: std::fmt::Debug, Clone);

    assert_eq!(type_name_of_val(&caller), "drive_actor_tests::EventCaller");
    assert_eq!(
        type_name_of_val(&caller.downgrade()),
        "drive_actor_tests::EventWeakCaller"
    );
    assert_eq!(type_name_of_val(&replyer), "drive_actor_tests::EventReplyer");

    let event_notified = Arc::new(Notify::new());
    tokio::spawn({
        let event_notified = event_notified.clone();
        async move {
            while let Some(command) = replyer.recv().await {
                assert_eq!(command.0, "Event_struct");
                event_notified.notify_one();
            }
        }
    });

    {
        let weak_caller = caller.downgrade();
        let upgraded_caller = weak_caller.upgrade().unwrap();
        upgraded_caller.event("Event_struct".to_string());
        event_notified.notified().await;
    }

    caller.event("Event_struct".to_string());
    event_notified.notified().await;
}

#[tokio::test]
async fn test_event_struct_weak_upgrade_none_after_drop() {
    let weak_caller = {
        let (event_caller, _event_replyer) = Event::actor();
        event_caller.downgrade()
    };

    assert!(weak_caller.upgrade().is_none());
}

#[tokio::test]
async fn test_caller_closed_after_replyer_dropped() {
    let (caller, replyer) = GetVoid::actor();
    drop(replyer);
    caller.as_ref().closed().await;
}