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<'a>(&'a self) -> &'a str;
44}
45
46pub struct OperationExecutor<
47 CTX: Send,
48 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
49 + Into<Vec<ParametersParameter>>
50 + Send,
51 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError> + Into<Resource> + Send,
52> {
53 _ctx: std::marker::PhantomData<CTX>,
54 code: String,
55 executor: Arc<
56 Box<
57 dyn Fn(
58 CTX,
59 TenantId,
60 ProjectId,
61 &InvocationRequest,
62 I,
63 )
64 -> Pin<Box<dyn Future<Output = Result<O, OperationOutcomeError>> + Send>>
65 + Send
66 + Sync,
67 >,
68 >,
69}
70
71impl<
72 CTX: Send,
73 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
74 + Into<Vec<ParametersParameter>>
75 + Send,
76 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError> + Into<Resource> + Send,
77> OperationExecutor<CTX, I, O>
78{
79 pub fn new(
80 code: String,
81 executor: Box<
82 dyn Fn(
83 CTX,
84 TenantId,
85 ProjectId,
86 &InvocationRequest,
87 I,
88 )
89 -> Pin<Box<dyn Future<Output = Result<O, OperationOutcomeError>> + Send>>
90 + Send
91 + Sync,
92 >,
93 ) -> Self {
94 Self {
95 _ctx: std::marker::PhantomData,
96 executor: Arc::new(executor),
97 code,
98 }
99 }
100}
101
102impl<
103 CTX: Send + Sync + 'static,
104 I: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
105 + Into<Vec<ParametersParameter>>
106 + Send
107 + 'static,
108 O: TryFrom<Vec<ParametersParameter>, Error = OperationOutcomeError>
109 + Into<Resource>
110 + Send
111 + 'static,
112> OperationInvocation<CTX> for OperationExecutor<CTX, I, O>
113{
114 fn execute<'a>(
115 &self,
116 ctx: CTX,
117 tenant: TenantId,
118 project: ProjectId,
119 request: &'a InvocationRequest,
120 ) -> Pin<Box<dyn Future<Output = Result<Resource, OperationOutcomeError>> + Send + 'a>> {
121 let executor = self.executor.clone();
122 Box::pin(async move {
123 let parameters = match request {
124 InvocationRequest::Instance(instance_request) => &instance_request.parameters,
125 InvocationRequest::Type(type_request) => &type_request.parameters,
126 InvocationRequest::System(system_request) => &system_request.parameters,
127 };
128
129 let input = I::try_from(parameters.parameter.clone().unwrap_or_default())?;
130
131 let output = (executor)(ctx, tenant, project, request, input).await?;
132
133 Ok(output.into())
134 })
135 }
136
137 fn code<'a>(&'a self) -> &'a str {
138 &self.code
139 }
140}