1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use super::*;

pub trait TUnitOfWorkCommandHandler: Send + Sync {
	type Dependency;
	fn destruct(self) -> Self::Dependency;
}

impl<D1, D2> TUnitOfWorkCommandHandler for CommandHandler<(D1, D2)>
where
	D1: crate::prelude::TCommand,
	D2: crate::prelude::TSetCurrentEvents + crate::prelude::TUnitOfWork,
{
	type Dependency = (D1, D2);

	fn destruct(self) -> Self::Dependency {
		self.0
	}
}

impl<T, R, E, D1, D2> TCommandService<R, E> for T
where
	R: ApplicationResponse,
	E: ApplicationError + std::convert::From<crate::responses::BaseError> + std::convert::Into<BaseError> + Clone,
	D1: TCommand + for<'a> TGetHandler<&'a mut D2, Result<R, E>>,
	D2: TSetCurrentEvents + TUnitOfWork,
	T: TUnitOfWorkCommandHandler<Dependency = (D1, D2)>,
{
	async fn execute(self) -> Result<R, E> {
		let (cmd, mut dep) = self.destruct();

		dep.begin().await?;

		let result = (D1::get_handler())(cmd, &mut dep).await;
		match result {
			Ok(val) => {
				dep.commit().await?;
				dep.close().await;

				Ok(val)
			}
			// TODO This code only processes events that can be externally notified. Need to develop
			Err(err) => {
				dep.rollback().await?;
				dep.close().await;

				if let BaseError::StopSentinelWithEvent(event) = err.clone().into() {
					dep.set_current_events(vec![event.clone()].into());
					dep.process_internal_events().await?;
					dep.process_external_events().await?;
					Err(BaseError::StopSentinelWithEvent(event).into())
				} else {
					Err(err)
				}
			}
		}
	}
}

#[macro_export]
#[doc(hidden)]
macro_rules! __register_uow_services_internal {
    (
        $messagebus:ty,
        $response:ty,
        $error:ty,
        $h:expr,

        $(
            $command:ty => $handler:expr
        ),*
    ) => {
        use ruva::TUnitOfWorkCommandHandler;
        type ApplicationResult = std::result::Result<$response,$error>;

        $(
            impl<'a> ruva::TGetHandler<&'a mut ::ruva::Context, ApplicationResult> for $command {
                fn get_handler() -> impl ::ruva::AsyncFunc<$command, &'a mut ::ruva::Context, ApplicationResult > {
                    $handler
                }
            }

            impl ::ruva::TMessageBus<$response,$error,$command> for $messagebus{
                fn command_handler(
                    &self,
                    context_manager: ruva::AtomicContextManager,
                    cmd: $command,
                ) -> impl ::ruva::TCommandService<$response, $error> {
                    $h(::ruva::CommandHandler((cmd, ::ruva::Context::new(context_manager))))
                }
            }
        )*
    };
}

#[macro_export]
macro_rules! register_uow_services {
    // Case with custom handler function
    (
        $messagebus:ty,
        $response:ty,
        $error:ty,
        $h:expr,

        $(
            $command:ty => $handler:expr
        ),*
    ) => {
       	ruva::__register_uow_services_internal!($messagebus, $response, $error, $h, $($command => $handler),*);
    };

    // Default case
    (
        $messagebus:ty,
        $response:ty,
        $error:ty,

        $(
            $command:ty => $handler:expr
        ),*
    ) => {
        ruva::__register_uow_services_internal!($messagebus, $response, $error, ::std::convert::identity, $($command => $handler),*);
    };
}