1use dicom_core::{ops::AttributeSelector, Tag};
4use dicom_json::DicomJson;
5use dicom_object::InMemDicomObject;
6
7use snafu::ResultExt;
8
9use crate::{
10 apply_auth_and_headers, selector_to_string, validate_dicom_json_content_type,
11 DeserializationFailedSnafu, DicomWebClient, DicomWebError, RequestFailedSnafu,
12};
13
14#[derive(Debug, Clone)]
17pub struct MwlRequest {
18 client: DicomWebClient,
19 url: String,
20
21 limit: Option<u32>,
22 offset: Option<u32>,
23 includefields: Vec<Tag>,
24 fuzzymatching: Option<bool>,
25 filters: Vec<(AttributeSelector, String)>,
26}
27
28impl MwlRequest {
29 fn new(client: DicomWebClient, url: String) -> Self {
30 MwlRequest {
31 client,
32 url,
33 limit: None,
34 offset: None,
35 includefields: vec![],
36 fuzzymatching: None,
37 filters: vec![],
38 }
39 }
40
41 pub async fn run(&self) -> Result<Vec<InMemDicomObject>, DicomWebError> {
43 let mut query: Vec<(String, String)> = vec![];
44 if let Some(limit) = self.limit {
45 query.push((String::from("limit"), limit.to_string()));
46 }
47 if let Some(offset) = self.offset {
48 query.push((String::from("offset"), offset.to_string()));
49 }
50 if let Some(fuzzymatching) = self.fuzzymatching {
51 query.push((String::from("fuzzymatching"), fuzzymatching.to_string()));
52 }
53 for include_field in self.includefields.iter() {
54 let radix_string = format!(
56 "{:04x}{:04x}",
57 include_field.group(),
58 include_field.element()
59 );
60
61 query.push((String::from("includefield"), radix_string));
62 }
63 for (selector, value) in self.filters.iter() {
64 query.push((selector_to_string(&selector), value.clone()));
65 }
66
67 let mut request = self.client.client.get(&self.url).query(&query);
68 request = apply_auth_and_headers(request, &self.client);
69
70 let response = request
71 .send()
72 .await
73 .context(RequestFailedSnafu { url: &self.url })?;
74
75 if !response.status().is_success() {
76 return Err(DicomWebError::HttpStatusFailure {
77 status_code: response.status(),
78 });
79 }
80
81 let ct = response
83 .headers()
84 .get("Content-Type")
85 .ok_or(DicomWebError::MissingContentTypeHeader)?;
86 validate_dicom_json_content_type(ct.to_str().unwrap_or_default())?;
87
88 Ok(response
89 .json::<Vec<DicomJson<InMemDicomObject>>>()
90 .await
91 .context(DeserializationFailedSnafu {})?
92 .into_iter()
93 .map(|dj| dj.into_inner())
94 .collect())
95 }
96
97 pub fn with_limit(&mut self, limit: u32) -> &mut Self {
100 self.limit = Some(limit);
101 self
102 }
103
104 pub fn with_offset(&mut self, offset: u32) -> &mut Self {
107 self.offset = Some(offset);
108 self
109 }
110
111 pub fn with_includefields(&mut self, includefields: Vec<Tag>) -> &mut Self {
113 self.includefields = includefields;
114 self
115 }
116
117 pub fn with_fuzzymatching(&mut self, fuzzymatching: bool) -> &mut Self {
119 self.fuzzymatching = Some(fuzzymatching);
120 self
121 }
122
123 pub fn with_filter(&mut self, selector: AttributeSelector, value: String) -> &mut Self {
125 self.filters.push((selector, value));
126 self
127 }
128}
129
130impl DicomWebClient {
131 pub fn query_modality_scheduled_procedure_steps(&self) -> MwlRequest {
133 let base_url = &self.qido_url;
134 let url = format!("{base_url}/modality-scheduled-procedure-steps");
135
136 MwlRequest::new(self.clone(), url)
137 }
138}