use dicom_core::ops::AttributeSelector;
use dicom_json::DicomJson;
use dicom_object::InMemDicomObject;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use crate::{
apply_auth_and_headers, selector_to_string, validate_dicom_json_content_type,
DeserializationFailedSnafu, DicomWebClient, DicomWebError, RequestFailedSnafu,
};
#[derive(Debug, Clone)]
pub struct AsdoSendRequest {
client: DicomWebClient,
url: String,
destination: String,
username: Option<String>,
password: Option<String>,
token: Option<String>,
filters: Vec<(AttributeSelector, String)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct AuthInfo {
username: Option<String>,
password: Option<String>,
token: Option<String>,
}
impl AsdoSendRequest {
fn new(client: DicomWebClient, url: String) -> Self {
AsdoSendRequest {
client,
url,
filters: vec![],
destination: String::new(),
username: None,
password: None,
token: None,
}
}
pub async fn run(self) -> Result<InMemDicomObject, DicomWebError> {
let mut query: Vec<(String, String)> = vec![];
for (selector, value) in self.filters.iter() {
query.push((selector_to_string(&selector), value.clone()));
}
if self.destination.is_empty() {
return Err(DicomWebError::Other {
message: "Destination must be set for ASDO-RS request".to_string(),
});
}
query.push((String::from("destination"), self.destination.clone()));
let mut request = self.client.client.post(&self.url).query(&query);
if let (Some(username), Some(password)) = (&self.username, &self.password) {
request = request.json(&AuthInfo {
username: Some(username.clone()),
password: Some(password.clone()),
token: None,
});
} else if let Some(token) = &self.token {
request = request.json(&AuthInfo {
username: None,
password: None,
token: Some(token.clone()),
});
}
request = apply_auth_and_headers(request, &self.client);
let response = request
.send()
.await
.context(RequestFailedSnafu { url: &self.url })?;
if !response.status().is_success() {
return Err(DicomWebError::HttpStatusFailure {
status_code: response.status(),
});
}
let ct = response
.headers()
.get("Content-Type")
.ok_or(DicomWebError::MissingContentTypeHeader)?;
validate_dicom_json_content_type(ct.to_str().unwrap_or_default())?;
Ok(response
.json::<DicomJson<InMemDicomObject>>()
.await
.context(DeserializationFailedSnafu {})?
.into_inner())
}
pub fn with_filter(mut self, selector: AttributeSelector, value: String) -> Self {
self.filters.push((selector, value));
self
}
pub fn with_destination(mut self, destination: String) -> Self {
self.destination = destination;
self
}
pub fn with_basic_auth(mut self, username: String, password: String) -> Self {
self.username = Some(username);
self.password = Some(password);
self
}
pub fn with_bearer_token(mut self, token: String) -> Self {
self.token = Some(token);
self
}
}
#[derive(Debug, Clone)]
pub struct AsdoStatusRequest {
client: DicomWebClient,
url: String,
}
impl AsdoStatusRequest {
fn new(client: DicomWebClient, url: String) -> Self {
AsdoStatusRequest { client, url }
}
pub async fn run(&self) -> Result<InMemDicomObject, DicomWebError> {
let request = self.client.client.get(&self.url);
let request = apply_auth_and_headers(request, &self.client);
let response = request
.send()
.await
.context(RequestFailedSnafu { url: &self.url })?;
if !response.status().is_success() {
return Err(DicomWebError::HttpStatusFailure {
status_code: response.status(),
});
}
let ct = response
.headers()
.get("Content-Type")
.ok_or(DicomWebError::MissingContentTypeHeader)?;
validate_dicom_json_content_type(ct.to_str().unwrap_or_default())?;
Ok(response
.json::<DicomJson<InMemDicomObject>>()
.await
.context(DeserializationFailedSnafu {})?
.into_inner())
}
}
impl DicomWebClient {
pub fn send_studies(&self, transaction_uid: &str) -> AsdoSendRequest {
let base_url = &self.qido_url;
let url = format!("{base_url}/studies/send-requests/{transaction_uid}");
AsdoSendRequest::new(self.clone(), url)
}
pub fn send_studies_status(&self, transaction_uid: &str) -> AsdoStatusRequest {
let base_url = &self.qido_url;
let url = format!("{base_url}/studies/send-requests/{transaction_uid}");
AsdoStatusRequest::new(self.clone(), url)
}
pub fn send_series_in_study(
&self,
study_instance_uid: &str,
transaction_uid: &str,
) -> AsdoSendRequest {
let base_url = &self.qido_url;
let url = format!(
"{base_url}/studies/{study_instance_uid}/series/send-requests/{transaction_uid}"
);
AsdoSendRequest::new(self.clone(), url)
}
pub fn send_instances_in_study(
&self,
study_instance_uid: &str,
transaction_uid: &str,
) -> AsdoSendRequest {
let base_url = &self.qido_url;
let url = format!(
"{base_url}/studies/{study_instance_uid}/instances/send-requests/{transaction_uid}"
);
AsdoSendRequest::new(self.clone(), url)
}
pub fn send_instances_in_series(
&self,
study_instance_uid: &str,
series_instance_uid: &str,
transaction_uid: &str,
) -> AsdoSendRequest {
let base_url = &self.qido_url;
let url = format!(
"{base_url}/studies/{study_instance_uid}/series/{series_instance_uid}/instances/send-requests/{transaction_uid}",
);
AsdoSendRequest::new(self.clone(), url)
}
}