use reqwest::{
StatusCode,
header::{self, HeaderValue},
};
use serde::Serialize;
use super::{Client, Error};
use crate::{
extensions::{ParameterExt, ParameterValueExt, ParametersExt},
version::FhirVersion,
};
type ParametersParameter<V> = <<V as FhirVersion>::Parameters as ParametersExt>::Parameter;
type ParametersParameterValue<V> =
<<<V as FhirVersion>::Parameters as ParametersExt>::Parameter as ParameterExt>::Value;
#[derive(Debug, Clone)]
#[must_use = "You probably want to send the PATCH request"]
pub struct PatchViaFhir<'a, V: FhirVersion> {
client: Client<V>,
resource_type: V::ResourceType,
id: &'a str,
operations: Vec<Option<ParametersParameter<V>>>,
}
impl<'a, V: FhirVersion> PatchViaFhir<'a, V>
where
(StatusCode, V::OperationOutcome): Into<Error>,
{
pub(crate) const fn new(
client: Client<V>,
resource_type: V::ResourceType,
id: &'a str,
) -> Self {
Self { client, resource_type, id, operations: Vec::new() }
}
pub fn add<P, N>(mut self, path: P, name: N, value: ParametersParameter<V>) -> Self
where
P: Into<String>,
N: Into<String>,
{
#[allow(clippy::unwrap_used, reason = "We know the builder succeeds")]
let parameter = ParametersParameter::<V>::make(
"operation".to_owned(),
None,
vec![
Some(ParametersParameter::<V>::make(
"type".to_owned(),
Some(ParametersParameterValue::<V>::make_code("add".to_owned())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"path".to_owned(),
Some(ParametersParameterValue::<V>::make_string(path.into())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"name".to_owned(),
Some(ParametersParameterValue::<V>::make_string(name.into())),
vec![],
)),
Some(value),
],
);
self.operations.push(Some(parameter));
self
}
pub fn insert<P: Into<String>>(
mut self,
path: P,
value: ParametersParameter<V>,
index: i32,
) -> Self {
#[allow(clippy::unwrap_used, reason = "We know the builder succeeds")]
let parameter = ParametersParameter::<V>::make(
"operation".to_owned(),
None,
vec![
Some(ParametersParameter::<V>::make(
"type".to_owned(),
Some(ParametersParameterValue::<V>::make_code("insert".to_owned())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"path".to_owned(),
Some(ParametersParameterValue::<V>::make_string(path.into())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"index".to_owned(),
Some(ParametersParameterValue::<V>::make_integer(index)),
vec![],
)),
Some(value),
],
);
self.operations.push(Some(parameter));
self
}
pub fn delete<P: Into<String>>(mut self, path: P) -> Self {
#[allow(clippy::unwrap_used, reason = "We know the builder succeeds")]
let parameter = ParametersParameter::<V>::make(
"operation".to_owned(),
None,
vec![
Some(ParametersParameter::<V>::make(
"type".to_owned(),
Some(ParametersParameterValue::<V>::make_code("delete".to_owned())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"path".to_owned(),
Some(ParametersParameterValue::<V>::make_string(path.into())),
vec![],
)),
],
);
self.operations.push(Some(parameter));
self
}
pub fn replace<P: Into<String>>(mut self, path: P, value: ParametersParameter<V>) -> Self {
#[allow(clippy::unwrap_used, reason = "We know the builder succeeds")]
let parameter = ParametersParameter::<V>::make(
"operation".to_owned(),
None,
vec![
Some(ParametersParameter::<V>::make(
"type".to_owned(),
Some(ParametersParameterValue::<V>::make_code("replace".to_owned())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"path".to_owned(),
Some(ParametersParameterValue::<V>::make_string(path.into())),
vec![],
)),
Some(value),
],
);
self.operations.push(Some(parameter));
self
}
pub fn r#move<P: Into<String>>(mut self, path: P, source: i32, destination: i32) -> Self {
#[allow(clippy::unwrap_used, reason = "We know the builder succeeds")]
let parameter = ParametersParameter::<V>::make(
"operation".to_owned(),
None,
vec![
Some(ParametersParameter::<V>::make(
"type".to_owned(),
Some(ParametersParameterValue::<V>::make_code("move".to_owned())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"path".to_owned(),
Some(ParametersParameterValue::<V>::make_string(path.into())),
vec![],
)),
Some(ParametersParameter::<V>::make(
"source".to_owned(),
Some(ParametersParameterValue::<V>::make_integer(source)),
vec![],
)),
Some(ParametersParameter::<V>::make(
"destination".to_owned(),
Some(ParametersParameterValue::<V>::make_integer(destination)),
vec![],
)),
],
);
self.operations.push(Some(parameter));
self
}
pub async fn send(self) -> Result<(), Error> {
let parameters = V::Parameters::make(self.operations);
let url = self.client.url(&[self.resource_type.as_ref(), self.id]);
let request = self
.client
.0
.client
.patch(url)
.header(header::ACCEPT, V::MIME_TYPE)
.header(header::CONTENT_TYPE, HeaderValue::from_static(V::MIME_TYPE))
.json(¶meters);
let response = self.client.run_request(request).await?;
if response.status().is_success() {
Ok(())
} else {
Err(Error::from_response::<V>(response).await)
}
}
}
#[derive(Debug, Clone)]
#[must_use = "You probably want to send the PATCH request"]
pub struct PatchViaJson<'a, V: FhirVersion> {
client: Client<V>,
resource_type: V::ResourceType,
id: &'a str,
operations: Vec<serde_json::Map<String, serde_json::Value>>,
}
impl<'a, V: FhirVersion> PatchViaJson<'a, V>
where
(StatusCode, V::OperationOutcome): Into<Error>,
{
pub(crate) const fn new(
client: Client<V>,
resource_type: V::ResourceType,
id: &'a str,
) -> Self {
Self { client, resource_type, id, operations: Vec::new() }
}
pub fn add<P, I>(mut self, path: P, value: I) -> Result<Self, Error>
where
P: Into<String>,
I: Serialize,
{
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "add".into());
operation.insert("path".to_owned(), path.into().into());
operation.insert("value".to_owned(), serde_json::to_value(value)?);
self.operations.push(operation);
Ok(self)
}
pub fn remove<P: Into<String>>(mut self, path: P) -> Self {
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "remove".into());
operation.insert("path".to_owned(), path.into().into());
self.operations.push(operation);
self
}
pub fn test<P, I>(mut self, path: P, value: I) -> Result<Self, Error>
where
P: Into<String>,
I: Serialize,
{
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "test".into());
operation.insert("path".to_owned(), path.into().into());
operation.insert("value".to_owned(), serde_json::to_value(value)?);
self.operations.push(operation);
Ok(self)
}
pub fn replace<P, I>(mut self, path: P, value: I) -> Result<Self, Error>
where
P: Into<String>,
I: Serialize,
{
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "replace".into());
operation.insert("path".to_owned(), path.into().into());
operation.insert("value".to_owned(), serde_json::to_value(value)?);
self.operations.push(operation);
Ok(self)
}
pub fn r#move<PF, PT>(mut self, from: PF, path: PT) -> Self
where
PF: Into<String>,
PT: Into<String>,
{
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "move".into());
operation.insert("from".to_owned(), from.into().into());
operation.insert("path".to_owned(), path.into().into());
self.operations.push(operation);
self
}
pub fn copy<PF, PT>(mut self, from: PF, path: PT) -> Self
where
PF: Into<String>,
PT: Into<String>,
{
let mut operation = serde_json::Map::new();
operation.insert("op".to_owned(), "copy".into());
operation.insert("from".to_owned(), from.into().into());
operation.insert("path".to_owned(), path.into().into());
self.operations.push(operation);
self
}
pub async fn send(self) -> Result<(), Error> {
let url = self.client.url(&[self.resource_type.as_ref(), self.id]);
let request = self
.client
.0
.client
.patch(url)
.header(header::ACCEPT, V::MIME_TYPE)
.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json-patch+json"))
.json(&self.operations);
let response = self.client.run_request(request).await?;
if response.status().is_success() {
Ok(())
} else {
Err(Error::from_response::<V>(response).await)
}
}
}