1use std::{pin::Pin, sync::Arc};
2
3use haste_fhir_client::request::InvocationRequest;
4use haste_fhir_model::r4::generated::resources::{Parameters, ParametersParameter, Resource};
5use haste_fhir_operation_error::OperationOutcomeError;
6use haste_jwt::{ProjectId, TenantId};
7
8#[cfg(feature = "derive")]
9pub mod derive;
10
11pub enum Param<
12 T: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
13 + Into<Vec<ParametersParameter>>,
14> {
15 Value(T),
16 Parameters(Parameters),
17}
18
19impl<
20 T: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
21 + Into<Vec<ParametersParameter>>,
22> Param<T>
23{
24 pub fn as_parameters(self) -> Parameters {
25 match self {
26 Param::Value(v) => Parameters {
27 parameter: Some(v.into()),
28 ..Default::default()
29 },
30 Param::Parameters(p) => p,
31 }
32 }
33}
34
35pub trait OperationInvocation<CTX: Send>: Send + Sync {
36 fn execute<'a>(
37 &self,
38 ctx: CTX,
39 tenant: TenantId,
40 project: ProjectId,
41 request: &'a InvocationRequest,
42 ) -> Pin<Box<dyn Future<Output = Result<Resource, OperationOutcomeError>> + Send + 'a>>;
43 fn code(&self) -> &str;
44}
45
46type Executor<CTX, I, O> = Box<
47 dyn Fn(
48 CTX,
49 TenantId,
50 ProjectId,
51 &InvocationRequest,
52 I,
53 ) -> Pin<Box<dyn Future<Output = Result<O, OperationOutcomeError>> + Send>>
54 + Send
55 + Sync,
56>;
57
58pub struct OperationExecutor<
59 CTX: Send,
60 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
61 + Into<Vec<ParametersParameter>>
62 + Send,
63 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError> + Into<Resource> + Send,
64> {
65 _ctx: std::marker::PhantomData<CTX>,
66 code: String,
67 executor: Arc<Executor<CTX, I, O>>,
68}
69
70impl<
71 CTX: Send,
72 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
73 + Into<Vec<ParametersParameter>>
74 + Send,
75 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError> + Into<Resource> + Send,
76> OperationExecutor<CTX, I, O>
77{
78 #[must_use]
79 pub fn new(code: String, executor: Executor<CTX, I, O>) -> Self {
80 Self {
81 _ctx: std::marker::PhantomData,
82 executor: Arc::new(executor),
83 code,
84 }
85 }
86}
87
88impl<
89 CTX: Send + Sync + 'static,
90 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
91 + Into<Vec<ParametersParameter>>
92 + Send
93 + 'static,
94 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
95 + Into<Resource>
96 + Send
97 + 'static,
98> OperationInvocation<CTX> for OperationExecutor<CTX, I, O>
99{
100 fn execute<'a>(
101 &self,
102 ctx: CTX,
103 tenant: TenantId,
104 project: ProjectId,
105 request: &'a InvocationRequest,
106 ) -> Pin<Box<dyn Future<Output = Result<Resource, OperationOutcomeError>> + Send + 'a>> {
107 let executor = self.executor.clone();
108 Box::pin(async move {
109 let parameters = match request {
110 InvocationRequest::Instance(instance_request) => &instance_request.parameters,
111 InvocationRequest::Type(type_request) => &type_request.parameters,
112 InvocationRequest::System(system_request) => &system_request.parameters,
113 };
114
115 let input = I::try_from(parameters.parameter.clone().unwrap_or_default())?;
116
117 let output = (executor)(ctx, tenant, project, request, input).await?;
118
119 Ok(output.into())
120 })
121 }
122
123 fn code(&self) -> &str {
124 &self.code
125 }
126}