1use dicom_object::{FileDicomObject, InMemDicomObject};
3
4use rand::{distr::Alphanumeric, Rng};
5use snafu::ResultExt;
6
7use crate::{DicomWebClient, DicomWebError, RequestFailedSnafu};
8
9#[derive(Debug, Clone)]
11pub struct WadoStowRequest {
12 client: DicomWebClient,
13 url: String,
14 instances: Vec<FileDicomObject<InMemDicomObject>>,
15}
16
17impl WadoStowRequest {
18 fn new(client: DicomWebClient, url: String) -> Self {
19 WadoStowRequest {
20 client,
21 url,
22 instances: Vec::new(),
23 }
24 }
25
26 pub fn with_instances(mut self, instances: Vec<FileDicomObject<InMemDicomObject>>) -> Self {
27 self.instances = instances;
28 self
29 }
30
31 pub async fn run(&self) -> Result<(), DicomWebError> {
32 let mut request = self.client.client.post(&self.url);
33
34 if let Some(username) = &self.client.username {
36 request = request.basic_auth(username, self.client.password.as_ref());
37 }
38 else if let Some(bearer_token) = &self.client.bearer_token {
40 request = request.bearer_auth(bearer_token);
41 }
42
43 let boundary: String = rand::rng()
44 .sample_iter(&Alphanumeric)
45 .take(8)
46 .map(char::from)
47 .collect();
48
49 let mut multipart_buffer = vec![];
50 for instance in &self.instances {
51 let mut buffer = Vec::new();
52 instance.write_all(&mut buffer).unwrap();
53 multipart_buffer.extend_from_slice(b"--");
54 multipart_buffer.extend_from_slice(boundary.as_bytes());
55 multipart_buffer.extend_from_slice(b"\r\n");
56 multipart_buffer.extend_from_slice(b"Content-Type: application/dicom\r\n\r\n");
57
58 multipart_buffer.extend_from_slice(&buffer);
59 multipart_buffer.extend_from_slice(b"\r\n");
60 }
61 multipart_buffer.extend_from_slice(b"--");
63 multipart_buffer.extend_from_slice(boundary.as_bytes());
64 multipart_buffer.extend_from_slice(b"--\r\n");
65
66 let response = request
67 .header(
68 "Content-Type",
69 format!("multipart/related; type=\"application/dicom\"; boundary={}", boundary),
70 )
71 .body(multipart_buffer)
72 .send()
73 .await
74 .context(RequestFailedSnafu { url: &self.url })?;
75
76 if !response.status().is_success() {
77 return Err(DicomWebError::HttpStatusFailure {
78 status_code: response.status(),
79 });
80 }
81
82 Ok(())
83 }
84}
85
86impl DicomWebClient {
87 pub fn store_instances(&self) -> WadoStowRequest {
89 let url = format!("{}/studies", self.stow_url);
90 WadoStowRequest::new(self.clone(), url)
91 }
92
93 pub fn store_instances_in_study(&self, study_instance_uid: &str) -> WadoStowRequest {
95 let url = format!("{}/studies/{}", self.stow_url, study_instance_uid);
96 WadoStowRequest::new(self.clone(), url)
97 }
98}