haste_fhir_ops/
lib.rs

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}