#[macro_export]
macro_rules! gat_port {
(
$(#[$trait_meta:meta])*
$vis:vis trait $trait_name:ident {
$(
$(#[$method_meta:meta])*
async fn $method:ident( $($args:tt)* ) -> $ret:ty;
)*
}
) => {
$(#[$trait_meta])*
$vis trait $trait_name: ::std::marker::Send + ::std::marker::Sync {
$(
::paste::paste! {
$(#[$method_meta])*
#[doc = ""]
#[doc = concat!("Future type for `", stringify!($method), "` method")]
type [<$method:camel Future>]<'a>: ::std::future::Future<
Output = $crate::domain::DomainResult<$ret>
> + ::std::marker::Send + 'a
where
Self: 'a;
}
$(#[$method_meta])*
fn $method( $($args)* ) -> ::paste::paste! { Self::[<$method:camel Future>]<'_> };
)*
}
};
}
pub use gat_port;
#[cfg(test)]
mod tests {
use crate::domain::DomainResult;
use std::future::Future;
gat_port! {
pub trait TestUnitPort {
async fn do_something(&self) -> ();
}
}
gat_port! {
pub trait TestArgsPort {
async fn process(&self, id: u64, name: String) -> String;
}
}
gat_port! {
pub trait TestMutPort {
async fn mutate(&mut self, value: i32) -> ();
}
}
gat_port! {
pub trait TestMixedPort {
async fn read(&self) -> String;
async fn write(&mut self, data: String) -> ();
}
}
struct MockUnitPort;
impl TestUnitPort for MockUnitPort {
type DoSomethingFuture<'a>
= impl Future<Output = DomainResult<()>> + Send + 'a
where
Self: 'a;
fn do_something(&self) -> Self::DoSomethingFuture<'_> {
async move { Ok(()) }
}
}
struct MockArgsPort;
impl TestArgsPort for MockArgsPort {
type ProcessFuture<'a>
= impl Future<Output = DomainResult<String>> + Send + 'a
where
Self: 'a;
fn process(&self, _id: u64, name: String) -> Self::ProcessFuture<'_> {
async move { Ok(name) }
}
}
struct MockMutPort {
value: i32,
}
impl TestMutPort for MockMutPort {
type MutateFuture<'a>
= impl Future<Output = DomainResult<()>> + Send + 'a
where
Self: 'a;
fn mutate(&mut self, value: i32) -> Self::MutateFuture<'_> {
async move {
self.value = value;
Ok(())
}
}
}
struct MockMixedPort {
data: String,
}
impl TestMixedPort for MockMixedPort {
type ReadFuture<'a>
= impl Future<Output = DomainResult<String>> + Send + 'a
where
Self: 'a;
fn read(&self) -> Self::ReadFuture<'_> {
async move { Ok(self.data.clone()) }
}
type WriteFuture<'a>
= impl Future<Output = DomainResult<()>> + Send + 'a
where
Self: 'a;
fn write(&mut self, data: String) -> Self::WriteFuture<'_> {
async move {
self.data = data;
Ok(())
}
}
}
#[tokio::test]
async fn test_gat_port_unit() {
let port = MockUnitPort;
port.do_something().await.unwrap();
}
#[tokio::test]
async fn test_gat_port_args() {
let port = MockArgsPort;
let result = port.process(42, "test".to_string()).await.unwrap();
assert_eq!(result, "test");
}
#[tokio::test]
async fn test_gat_port_mut() {
let mut port = MockMutPort { value: 0 };
port.mutate(42).await.unwrap();
assert_eq!(port.value, 42);
}
#[tokio::test]
async fn test_gat_port_mixed() {
let mut port = MockMixedPort {
data: "initial".to_string(),
};
let value = port.read().await.unwrap();
assert_eq!(value, "initial");
port.write("updated".to_string()).await.unwrap();
let value = port.read().await.unwrap();
assert_eq!(value, "updated");
}
}