dicomweb_client/reqwest/
mod.rs

1use reqwest;
2use reqwest::header;
3use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
4
5pub use reqwest::Error;
6#[cfg(not(target_arch = "wasm32"))]
7use reqwest::Proxy;
8use serde::Serialize;
9use std::convert::TryFrom;
10use std::env;
11
12use crate::{DICOMQueryBuilder, DICOMwebClient};
13
14pub mod async_reqwest;
15pub mod blocking_reqwest;
16
17pub trait ReqwestClientBuilder {
18    type Client: ReqwestClient + Default;
19
20    fn new() -> Self;
21    fn proxy(self, proxy: Proxy) -> Self;
22    fn default_headers(self, headers: HeaderMap) -> Self;
23    fn build(self) -> reqwest::Result<Self::Client>;
24}
25
26pub trait ReqwestClient {
27    type ClientBuilder: ReqwestClientBuilder + Default;
28    type RequestBuilder: RequestBuilderTrait;
29
30    fn get<U: reqwest::IntoUrl>(&self, url: U) -> Self::RequestBuilder;
31}
32
33#[derive(Default)]
34pub struct DICOMwebClientReqwest<C, B> {
35    client: Option<C>,
36    config: Option<B>,
37    url: String,
38    qido_url_prefix: String,
39    wado_url_prefix: String,
40    stow_url_prefix: String,
41    ups_url_prefix: String,
42}
43
44impl<C: ReqwestClient, B: ReqwestClientBuilder<Client = C>> DICOMwebClient
45    for DICOMwebClientReqwest<C, B>
46{
47    type QueryBuilder = QueryBuilderReqwest<C::RequestBuilder>;
48
49    fn default_headers(mut self, key: &'static str, value: &str) -> Self {
50        let mut headers = header::HeaderMap::new();
51        headers.insert(key, value.parse().unwrap());
52
53        if let Some(client_builder) = self.config.take() {
54            self.config = Some(client_builder.default_headers(headers));
55        }
56        self
57    }
58
59    fn get_url(&mut self, url: &str) -> Self::QueryBuilder {
60        self.make_client();
61        let url = format!("{}{}", self.url, url);
62        QueryBuilderReqwest {
63            request_builder: self.client.as_ref().unwrap().get(url),
64        }
65    }
66
67    fn get_qido_prefix(&self) -> &str {
68        &self.qido_url_prefix
69    }
70    fn get_wado_prefix(&self) -> &str {
71        &self.wado_url_prefix
72    }
73
74    fn search_studies(&mut self) -> Self::QueryBuilder {
75        let url = format!("{}/studies", self.get_qido_prefix());
76        self.get_url(&url)
77    }
78
79    fn search_series(&mut self, study_instance_uid: &str) -> Self::QueryBuilder {
80        let url = format!(
81            "{}/studies/{}/series",
82            self.get_qido_prefix(),
83            study_instance_uid
84        );
85        self.get_url(&url)
86    }
87
88    fn search_instances(
89        &mut self,
90        study_instance_uid: &str,
91        series_instance_uid: &str,
92    ) -> Self::QueryBuilder {
93        let url = format!(
94            "{}/studies/{}/series/{}/instances",
95            self.get_qido_prefix(),
96            study_instance_uid,
97            series_instance_uid,
98        );
99        self.get_url(&url)
100    }
101
102    fn retrieve_instance(
103        &mut self,
104        study_instance_uid: &str,
105        series_instance_uid: &str,
106        sop_instance_uid: &str,
107    ) -> Self::QueryBuilder {
108        let url = format!(
109            "{}/studies/{}/series/{}/instances/{}",
110            self.get_wado_prefix(),
111            study_instance_uid,
112            series_instance_uid,
113            sop_instance_uid,
114        );
115        self.get_url(&url)
116    }
117}
118
119impl<C: ReqwestClient, B: ReqwestClientBuilder<Client = C>> DICOMwebClientReqwest<C, B> {
120    #[cfg(not(target_arch = "wasm32"))]
121    fn proxy(mut self, proxy: reqwest::Proxy) -> Self {
122        if let Some(client_builder) = self.config.take() {
123            self.config = Some(client_builder.proxy(proxy));
124        }
125        self
126    }
127
128    fn make_client(&mut self) {
129        if let Some(client_builder) = self.config.take() {
130            self.client = client_builder.build().ok();
131        }
132    }
133
134    pub fn new(url: &str) -> Self {
135        let client_builder = Some(B::new());
136        let mut dicomwebclient = Self {
137            client: None,
138            config: client_builder,
139            url: String::from(url),
140            qido_url_prefix: String::default(),
141            wado_url_prefix: String::default(),
142            stow_url_prefix: String::default(),
143            ups_url_prefix: String::default(),
144        };
145
146        #[cfg(not(target_arch = "wasm32"))]
147        if let Ok(proxy) = env::var("http_proxy") {
148            dicomwebclient = dicomwebclient.proxy(reqwest::Proxy::http(proxy).unwrap());
149        }
150
151        dicomwebclient
152    }
153}
154
155pub struct QueryBuilderReqwest<T> {
156    request_builder: T,
157}
158
159pub trait RequestBuilderTrait {
160    fn header<K, V>(self, key: K, value: V) -> Self
161    where
162        HeaderName: TryFrom<K>,
163        HeaderValue: TryFrom<V>,
164        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
165        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>;
166    fn query<T: Serialize + ?Sized>(self, query: &T) -> Self;
167}
168
169impl<T: RequestBuilderTrait> DICOMQueryBuilder for QueryBuilderReqwest<T> {
170    fn query(mut self, key: &str, value: &str) -> Self {
171        self.request_builder = self.request_builder.query(&[(key, value)]);
172        self
173    }
174
175    fn header(mut self, key: &str, value: &str) -> Self {
176        self.request_builder = self.request_builder.header(key, value);
177        self
178    }
179}
180
181impl<T: RequestBuilderTrait> QueryBuilderReqwest<T> {
182    pub fn header<K, V>(mut self, key: K, value: V) -> QueryBuilderReqwest<T>
183    where
184        HeaderName: TryFrom<K>,
185        <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
186        HeaderValue: TryFrom<V>,
187        <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
188    {
189        self.request_builder = self.request_builder.header(key, value);
190        self
191    }
192}