fhir_sdk/client/fhir/
write.rs

1//! Home of the ResourceWrite trait.
2
3use std::future::Future;
4
5use fhir_model::for_all_versions;
6use reqwest::StatusCode;
7use serde::Serialize;
8
9use super::{Client, Error};
10use crate::{
11	extensions::{AnyResource, GenericResource},
12	version::{FhirVersion, fhir_version},
13};
14
15/// A trait to write resources to the FHIR server, mutating the interior id and
16/// version_id so that the resource is up to date for future update requests.
17pub trait ResourceWrite<Version>: Serialize + Send + Sync {
18	/// Update the current version of the resource on the server. Returns
19	/// whether the resource was created.
20	fn update(
21		&mut self,
22		conditional: bool,
23		client: &Client<Version>,
24	) -> impl Future<Output = Result<bool, Error>> + Send;
25	/// Create this resource on the server. Returns the resource ID.
26	fn create(
27		&mut self,
28		client: &Client<Version>,
29	) -> impl Future<Output = Result<String, Error>> + Send;
30	/// Delete this resource on the FHIR server.
31	fn delete(self, client: &Client<Version>) -> impl Future<Output = Result<(), Error>> + Send;
32}
33
34impl<R, V> ResourceWrite<V> for R
35where
36	R: AnyResource<V> + Serialize + Send + Sync,
37	V: FhirVersion,
38	(StatusCode, V::OperationOutcome): Into<Error>,
39{
40	async fn update(&mut self, conditional: bool, client: &Client<V>) -> Result<bool, Error> {
41		let id = self.id().ok_or(Error::MissingId)?;
42		let version_id =
43			conditional.then(|| self.version_id().ok_or(Error::MissingVersionId)).transpose()?;
44		let (created, version_id) =
45			client.update_generic(R::TYPE_STR, id, self, version_id).await?;
46		self.set_version_id(version_id);
47		Ok(created)
48	}
49
50	async fn create(&mut self, client: &Client<V>) -> Result<String, Error> {
51		let (id, version_id) = client.create_generic(R::TYPE_STR, self).await?;
52		self.set_id(id.clone());
53		if let Some(version) = version_id {
54			self.set_version_id(version);
55		}
56		Ok(id)
57	}
58
59	async fn delete(self, client: &Client<V>) -> Result<(), Error> {
60		let id = self.id().ok_or(Error::MissingId)?;
61		client.delete(R::TYPE, id).await?;
62		Ok(())
63	}
64}
65
66/// A trait to write the resource enum to the FHIR server, mutating the interior
67/// id and version_id so that the resource is up to date for future update
68/// requests.
69/// This trait sadly needs to be separate to [`ResourceWrite`],
70/// because we cannot both implement the generic trait as well as implementing
71/// it on a type that fhir-model could implement the required traits for.
72pub trait AnyResourceWrite: Serialize + Send + Sync {
73	/// FHIR client version type.
74	type Version: FhirVersion;
75
76	/// Update the current version of the resource on the server. Returns
77	/// whether the resource was created.
78	fn update(
79		&mut self,
80		conditional: bool,
81		client: &Client<Self::Version>,
82	) -> impl Future<Output = Result<bool, Error>> + Send;
83	/// Create this resource on the server. Returns the resource ID.
84	fn create(
85		&mut self,
86		client: &Client<Self::Version>,
87	) -> impl Future<Output = Result<String, Error>> + Send;
88	/// Delete this resource on the FHIR server.
89	fn delete(
90		self,
91		client: &Client<Self::Version>,
92	) -> impl Future<Output = Result<(), Error>> + Send;
93}
94
95/// Implement `ResourceWrite` on generic resource enums. Sadly needs to be
96/// separate currently, see [AnyResource] (`Resource` can only implement
97/// `AnyResource<GenericResource>`).
98macro_rules! impl_generic_resource_write {
99	($version:ident) => {
100		use fhir_model::$version;
101
102		impl AnyResourceWrite for $version::resources::Resource {
103			type Version = fhir_version!($version);
104
105			async fn update(
106				&mut self,
107				conditional: bool,
108				client: &Client<Self::Version>,
109			) -> Result<bool, Error> {
110				let id = self.as_base_resource().id().as_deref().ok_or(Error::MissingId)?;
111				let version_id = conditional
112					.then(|| self.version_id().ok_or(Error::MissingVersionId))
113					.transpose()?;
114				let (created, version_id) = client
115					.update_generic(self.resource_type().as_str(), id, self, version_id)
116					.await?;
117				self.set_version_id(version_id);
118				Ok(created)
119			}
120
121			async fn create(&mut self, client: &Client<Self::Version>) -> Result<String, Error> {
122				let (id, version_id) =
123					client.create_generic(self.resource_type().as_str(), self).await?;
124				self.as_base_resource_mut().set_id(Some(id.clone()));
125				if let Some(version) = version_id {
126					self.set_version_id(version);
127				}
128				Ok(id)
129			}
130
131			async fn delete(self, client: &Client<Self::Version>) -> Result<(), Error> {
132				let id = self.as_base_resource().id().as_deref().ok_or(Error::MissingId)?;
133				client.delete(self.resource_type(), id).await?;
134				Ok(())
135			}
136		}
137	};
138}
139for_all_versions!(impl_generic_resource_write);