Skip to main content

haste_server/fhir_client/middleware/
mod.rs

1//! The server's request pipeline.
2//!
3//! A request runs through a [`Pipeline`]: its [`Middleware`] layers in order,
4//! then the [`Handler`] that ends it. A layer gets the request and a [`Next`]
5//! that runs the rest of the pipeline, so it can check or change the request
6//! before and the response after. The handler at the end always produces a
7//! response, so a layer that runs `next` always gets one back.
8//!
9//! `Next` borrows the layers that follow and the handler, so running it costs
10//! no allocation or reference counting. Everything is generic over one
11//! [`Backend`], so a layer names no repository, search or terminology type.
12
13use crate::fhir_client::{ClientState, ServerCTX};
14use haste_fhir_client::{
15    FHIRClient,
16    request::{FHIRRequest, FHIRResponse},
17};
18use haste_fhir_operation_error::OperationOutcomeError;
19use haste_fhir_search::SearchEngine;
20use haste_fhir_terminology::FHIRTerminology;
21use haste_repository::Repository;
22use std::{pin::Pin, sync::Arc};
23
24pub mod auditing;
25pub mod auth_z;
26pub mod canonical_cache;
27pub mod capabilities;
28pub mod check_project;
29pub mod custom_models;
30pub mod element_filtering;
31pub mod operation_definitions;
32pub mod operations;
33pub mod patch_as_update;
34pub mod rate_limit;
35pub mod search_includes;
36pub mod set_artifact_tenant;
37pub mod storage;
38pub mod tenant_tier_limits;
39pub mod transaction;
40pub mod validation;
41
42/// The server client and the stack it runs on: its repository, search engine
43/// and terminology. The client implements it, so a layer is generic over the
44/// one type that names them all, and `ctx.client` sends requests back through
45/// the whole pipeline.
46pub trait Backend:
47    FHIRClient<Arc<ServerCTX<Self>>, OperationOutcomeError> + Sized + 'static
48{
49    type Repo: Repository + Send + Sync + 'static;
50    type Search: SearchEngine + Send + Sync + 'static;
51    type Terminology: FHIRTerminology + Send + Sync + 'static;
52
53    /// The OperationDefinitions of the server's built-in operations.
54    fn builtin_operations(&self) -> &operation_definitions::Builtins;
55}
56
57/// The future a layer or handler returns: the response, once the rest of the
58/// pipeline has run. It borrows the pipeline and the state for `'a`.
59pub type Output<'a> =
60    Pin<Box<dyn Future<Output = Result<FHIRResponse, OperationOutcomeError>> + Send + 'a>>;
61
62/// A layer of the pipeline. It runs `next` for the response, or answers the
63/// request itself.
64pub trait Middleware<B: Backend>: Send + Sync {
65    fn call<'a>(
66        &'a self,
67        state: &'a ClientState<B>,
68        ctx: Arc<ServerCTX<B>>,
69        request: FHIRRequest,
70        next: Next<'a, B>,
71    ) -> Output<'a>;
72}
73
74/// The end of a pipeline: what answers the request once every layer has run.
75pub trait Handler<B: Backend>: Send + Sync {
76    fn handle<'a>(
77        &'a self,
78        state: &'a ClientState<B>,
79        ctx: Arc<ServerCTX<B>>,
80        request: FHIRRequest,
81    ) -> Output<'a>;
82}
83
84/// The rest of the pipeline from a layer's point of view: the layers after it
85/// and the handler. Copy, so a layer can run it more than once.
86pub struct Next<'a, B: Backend> {
87    rest: &'a [Box<dyn Middleware<B>>],
88    end: &'a dyn Handler<B>,
89}
90
91impl<B: Backend> Clone for Next<'_, B> {
92    fn clone(&self) -> Self {
93        *self
94    }
95}
96
97impl<B: Backend> Copy for Next<'_, B> {}
98
99impl<'a, B: Backend> Next<'a, B> {
100    /// Runs the rest of the pipeline on `request`.
101    pub fn run(
102        self,
103        state: &'a ClientState<B>,
104        ctx: Arc<ServerCTX<B>>,
105        request: FHIRRequest,
106    ) -> Output<'a> {
107        match self.rest.split_first() {
108            Some((layer, rest)) => layer.call(
109                state,
110                ctx,
111                request,
112                Next {
113                    rest,
114                    end: self.end,
115                },
116            ),
117            None => self.end.handle(state, ctx, request),
118        }
119    }
120}
121
122/// Layers in order, then the handler that ends the pipeline.
123pub struct Pipeline<B: Backend> {
124    layers: Box<[Box<dyn Middleware<B>>]>,
125    end: Box<dyn Handler<B>>,
126}
127
128impl<B: Backend> Pipeline<B> {
129    pub fn new(layers: Vec<Box<dyn Middleware<B>>>, end: impl Handler<B> + 'static) -> Self {
130        Pipeline {
131            layers: layers.into_boxed_slice(),
132            end: Box::new(end),
133        }
134    }
135
136    /// Runs `request` through every layer and the handler.
137    pub fn run<'a>(
138        &'a self,
139        state: &'a ClientState<B>,
140        ctx: Arc<ServerCTX<B>>,
141        request: FHIRRequest,
142    ) -> Output<'a> {
143        Next {
144            rest: &self.layers,
145            end: self.end.as_ref(),
146        }
147        .run(state, ctx, request)
148    }
149}
150
151/// A pipeline ends another: the router's lanes are pipelines of their own.
152impl<B: Backend> Handler<B> for Pipeline<B> {
153    fn handle<'a>(
154        &'a self,
155        state: &'a ClientState<B>,
156        ctx: Arc<ServerCTX<B>>,
157        request: FHIRRequest,
158    ) -> Output<'a> {
159        self.run(state, ctx, request)
160    }
161}