Skip to main content

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(&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}