htsget_http/
post_request.rs

1use serde::{Deserialize, Serialize};
2use tracing::instrument;
3
4use htsget_config::types::{Format, Query, Request};
5
6use crate::{Endpoint, QueryBuilder, Result, match_format};
7
8/// A struct to represent a POST request according to the
9/// [HtsGet specification](https://samtools.github.io/hts-specs/htsget.html). It implements
10/// [Deserialize] to make it more ergonomic. Each `PostRequest` can contain several regions.
11#[derive(Serialize, Deserialize, Debug, Default)]
12pub struct PostRequest {
13  pub format: Option<String>,
14  pub class: Option<String>,
15  pub fields: Option<Vec<String>>,
16  pub tags: Option<Vec<String>>,
17  pub notags: Option<Vec<String>>,
18  pub regions: Option<Vec<Region>>,
19}
20
21/// A struct that contains the data to quest for a specific region. It is only meant to be use
22/// alongside a `PostRequest`
23#[derive(Serialize, Deserialize, Debug)]
24pub struct Region {
25  #[serde(rename = "referenceName")]
26  pub reference_name: String,
27  pub start: Option<u32>,
28  pub end: Option<u32>,
29}
30
31impl PostRequest {
32  /// Converts the `PostRequest` into one or more equivalent [Queries](Query)
33  #[instrument(level = "trace", skip_all, ret)]
34  pub(crate) fn get_queries(self, request: Request, endpoint: &Endpoint) -> Result<Vec<Query>> {
35    let format = match_format(endpoint, self.format.clone())?;
36
37    if let Some(ref regions) = self.regions {
38      regions
39        .iter()
40        .map(|region| {
41          Ok(
42            self
43              .get_base_query_builder(request.clone(), format)?
44              .with_reference_name(Some(region.reference_name.clone()))
45              .with_range_from_u32(region.start, region.end)?
46              .build(),
47          )
48        })
49        .collect::<Result<Vec<Query>>>()
50    } else {
51      Ok(vec![self.get_base_query_builder(request, format)?.build()])
52    }
53  }
54
55  fn get_base_query_builder(&self, request: Request, format: Format) -> Result<QueryBuilder> {
56    QueryBuilder::new(request, format)
57      .with_class(self.class.clone())?
58      .with_fields_from_vec(self.fields.clone())
59      .with_tags_from_vec(self.tags.clone(), self.notags.clone())
60  }
61}
62
63#[cfg(test)]
64mod tests {
65  use htsget_config::types::{Class, Format};
66
67  use super::*;
68
69  #[test]
70  fn post_request_without_regions() {
71    let request = Request::new_with_id("id".to_string());
72
73    assert_eq!(
74      PostRequest {
75        format: Some("VCF".to_string()),
76        class: Some("header".to_string()),
77        fields: None,
78        tags: None,
79        notags: None,
80        regions: None,
81      }
82      .get_queries(request.clone(), &Endpoint::Variants)
83      .unwrap(),
84      vec![Query::new("id", Format::Vcf, request).with_class(Class::Header)]
85    );
86  }
87
88  #[test]
89  fn post_request_with_one_region() {
90    let request = Request::new_with_id("id".to_string());
91
92    assert_eq!(
93      PostRequest {
94        format: Some("VCF".to_string()),
95        class: Some("header".to_string()),
96        fields: None,
97        tags: None,
98        notags: None,
99        regions: Some(vec![Region {
100          reference_name: "20".to_string(),
101          start: Some(150),
102          end: Some(153),
103        }]),
104      }
105      .get_queries(request.clone(), &Endpoint::Variants)
106      .unwrap(),
107      vec![
108        Query::new("id", Format::Vcf, request)
109          .with_class(Class::Header)
110          .with_reference_name("20".to_string())
111          .with_start(150)
112          .with_end(153)
113      ]
114    );
115  }
116
117  #[test]
118  fn post_request_with_regions() {
119    let request = Request::new_with_id("id".to_string());
120
121    assert_eq!(
122      PostRequest {
123        format: Some("VCF".to_string()),
124        class: Some("header".to_string()),
125        fields: None,
126        tags: None,
127        notags: None,
128        regions: Some(vec![
129          Region {
130            reference_name: "20".to_string(),
131            start: Some(150),
132            end: Some(153),
133          },
134          Region {
135            reference_name: "11".to_string(),
136            start: Some(152),
137            end: Some(154),
138          }
139        ]),
140      }
141      .get_queries(request.clone(), &Endpoint::Variants)
142      .unwrap(),
143      vec![
144        Query::new("id", Format::Vcf, request.clone())
145          .with_class(Class::Header)
146          .with_reference_name("20".to_string())
147          .with_start(150)
148          .with_end(153),
149        Query::new("id", Format::Vcf, request)
150          .with_class(Class::Header)
151          .with_reference_name("11".to_string())
152          .with_start(152)
153          .with_end(154)
154      ]
155    );
156  }
157}