dicomweb_client/
async_surf.rs

1use crate::{DICOMQueryBuilder, Error, Result};
2use bytes::{Buf, Bytes};
3use dicom::object::{DefaultDicomObject, InMemDicomObject};
4use dicomweb_util::{dicom_from_reader, json2dicom, parse_multipart_body};
5use log::debug;
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::collections::HashMap;
10use std::{convert::TryInto, fmt::format, io::Cursor, marker::PhantomData};
11use surf::Url;
12
13use crate::DICOMwebClient;
14
15impl From<surf::Error> for crate::Error {
16    fn from(e: surf::Error) -> Self {
17        crate::Error::Surf(e)
18    }
19}
20
21#[derive(Default, Debug)]
22pub struct Client {
23    client: surf::Client,
24    config: surf::Config,
25    url: Option<Url>,
26    qido_url_prefix: String,
27    wado_url_prefix: String,
28    stow_url_prefix: String,
29    ups_url_prefix: String,
30}
31
32impl DICOMwebClient for Client {
33    type QueryBuilder = QueryBuilder;
34
35    fn default_headers(mut self, key: &'static str, value: &str) -> Self {
36        self.config = self.config.add_header(key, value).unwrap();
37        self
38    }
39
40    fn get_url(&mut self, url: &str) -> Self::QueryBuilder {
41        let mut newurl = self.url.clone().unwrap();
42        let mut basepath = newurl.path();
43        if basepath == "/" {
44            basepath = "";
45        }
46        let path = format!("{}{}", basepath, url);
47        newurl.set_path(&path.as_str());
48        QueryBuilder {
49            request_builder: self.client.get(newurl),
50            query: Default::default(),
51        }
52    }
53
54    fn get_qido_prefix(&self) -> &str {
55        &self.qido_url_prefix
56    }
57    fn get_wado_prefix(&self) -> &str {
58        &self.wado_url_prefix
59    }
60}
61
62impl Client {
63    pub fn new(url: &str) -> Self {
64        let config = surf::Config::new();
65        let client = surf::Client::new();
66        Self {
67            client,
68            config,
69            url: Some(Url::parse(&url).unwrap()),
70            ..Default::default()
71        }
72    }
73
74    // #[cfg(not(target_arch = "wasm32"))]
75    // fn proxy(mut self, proxy: reqwest::Proxy) -> Self {
76    //     self.client_builder = self.client_builder.proxy(proxy);
77    //     self
78    // }
79}
80
81pub struct QueryBuilder {
82    query: HashMap<String, String>,
83    request_builder: surf::RequestBuilder,
84}
85
86impl DICOMQueryBuilder for QueryBuilder {
87    fn query(mut self, key: &str, value: &str) -> Self {
88        self.query.insert(key.to_string(), value.to_string());
89        self
90    }
91
92    fn header(mut self, key: &str, value: &str) -> Self {
93        self.request_builder = self.request_builder.header(key, value);
94        self
95    }
96}
97
98impl QueryBuilder {
99    pub async fn results(self) -> Result<Vec<InMemDicomObject>> {
100        let req = self.request_builder.query(&self.query)?;
101        debug!("req: {:?}", req);
102        let mut res = req.send().await?;
103        let content_type = res.header("content-type").unwrap().get(0).unwrap();
104        println!("content-type: {}", content_type);
105
106        if !content_type.as_str().starts_with("application/dicom+json") {
107            panic!(
108                "invalid content type, should be application/dicom+json,  response: {:?}",
109                res
110            )
111            // return Err(Error::DICOMweb(
112            //     "invalid content type, should be application/dicom+json".to_string(),
113            // ));
114        }
115
116        let json: Vec<Value> = res.body_json().await?;
117        Ok(json2dicom(&json)?)
118    }
119
120    pub async fn dicoms(self) -> Result<Vec<DefaultDicomObject>> {
121        let mut res = self.request_builder.query(&self.query)?.send().await?;
122        let content_type = res.header("content-type").unwrap().get(0).unwrap();
123        println!("content-type: {}", content_type);
124        let (_, boundary) = content_type.as_str().rsplit_once("boundary=").unwrap();
125        let boundary = String::from(boundary);
126        println!("boundary: {}", boundary);
127
128        let body: Bytes = res.body_bytes().await?.into();
129        let parts = parse_multipart_body(body, &boundary)?;
130        let result = parts
131            .iter()
132            .map(|part| {
133                let reader = Cursor::new(part).reader();
134                dicom_from_reader(reader).unwrap()
135            })
136            .collect();
137        Ok(result)
138    }
139}