Skip to main content

htsget_config/
types.rs

1//! Types related to htsget like formats, reference names, classes or intervals.
2//!
3
4use crate::config::advanced::auth::AuthorizationRule;
5#[cfg(feature = "experimental")]
6use crate::encryption_scheme::EncryptionScheme;
7use crate::error::Error;
8use crate::error::Error::ParseError;
9use http::HeaderMap;
10use noodles::core::Position;
11use noodles::core::region::Interval as NoodlesInterval;
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use std::cmp::{Ordering, max, min};
15use std::collections::{HashMap, HashSet};
16use std::fmt::{Debug, Display, Formatter};
17use std::{fmt, io, result};
18use thiserror::Error;
19use tracing::instrument;
20
21/// The result type returning a `HtsGetError`.
22pub type Result<T> = result::Result<T, HtsGetError>;
23
24/// An enumeration with all the possible formats.
25#[derive(JsonSchema, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
26#[serde(rename_all(serialize = "UPPERCASE"), deny_unknown_fields)]
27pub enum Format {
28  #[default]
29  #[serde(alias = "bam", alias = "BAM")]
30  Bam,
31  #[serde(alias = "cram", alias = "CRAM")]
32  Cram,
33  #[serde(alias = "vcf", alias = "VCF")]
34  Vcf,
35  #[serde(alias = "bcf", alias = "BCF")]
36  Bcf,
37}
38
39/// Todo allow these to be configurable.
40impl Format {
41  /// Get the file ending for the format.
42  pub fn file_ending(&self) -> &str {
43    match self {
44      Format::Bam => ".bam",
45      Format::Cram => ".cram",
46      Format::Vcf => ".vcf.gz",
47      Format::Bcf => ".bcf",
48    }
49  }
50
51  /// Get the file name including its ending.
52  pub fn fmt_file(&self, id: &str) -> String {
53    format!("{id}{}", self.file_ending())
54  }
55
56  /// Get the index file ending for this format.
57  pub fn index_file_ending(&self) -> &str {
58    match self {
59      Format::Bam => ".bam.bai",
60      Format::Cram => ".cram.crai",
61      Format::Vcf => ".vcf.gz.tbi",
62      Format::Bcf => ".bcf.csi",
63    }
64  }
65
66  /// Get the index file name including its ending.
67  pub fn fmt_index(&self, id: &str) -> String {
68    format!("{id}{}", self.index_file_ending())
69  }
70
71  /// Get the GZI index file ending for this format.
72  pub fn gzi_index_file_ending(&self) -> io::Result<&str> {
73    match self {
74      Format::Bam => Ok(".bam.gzi"),
75      Format::Cram => Err(io::Error::other("CRAM does not support GZI".to_string())),
76      Format::Vcf => Ok(".vcf.gz.gzi"),
77      Format::Bcf => Ok(".bcf.gzi"),
78    }
79  }
80
81  /// Get the GZI index file name including its ending.
82  pub fn fmt_gzi(&self, id: &str) -> io::Result<String> {
83    Ok(format!("{id}{}", self.gzi_index_file_ending()?))
84  }
85
86  /// Check if the id points at an index file.
87  pub fn is_index(id: &str) -> bool {
88    id.ends_with(".bai")
89      || id.ends_with(".crai")
90      || id.ends_with(".tbi")
91      || id.ends_with(".csi")
92      || id.ends_with(".gzi")
93  }
94}
95
96impl From<Format> for String {
97  fn from(format: Format) -> Self {
98    format.to_string()
99  }
100}
101
102impl Display for Format {
103  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
104    match self {
105      Format::Bam => write!(f, "BAM"),
106      Format::Cram => write!(f, "CRAM"),
107      Format::Vcf => write!(f, "VCF"),
108      Format::Bcf => write!(f, "BCF"),
109    }
110  }
111}
112
113/// Class component of htsget response.
114#[derive(Copy, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
115#[serde(rename_all(serialize = "lowercase"), deny_unknown_fields)]
116pub enum Class {
117  #[serde(alias = "header", alias = "HEADER")]
118  Header,
119  #[default]
120  #[serde(alias = "body", alias = "BODY")]
121  Body,
122}
123
124/// An interval represents the start (0-based, inclusive) and end (0-based exclusive) ranges of the
125/// query.
126#[derive(JsonSchema, Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
127#[serde(deny_unknown_fields)]
128pub struct Interval {
129  /// The start interval (0-based, inclusive).
130  #[serde(skip_serializing_if = "Option::is_none")]
131  start: Option<u32>,
132  /// The end interval (0-based, exclusive).
133  #[serde(skip_serializing_if = "Option::is_none")]
134  end: Option<u32>,
135}
136
137impl Interval {
138  /// Does this interval contain the passed in interval. Returns the passed in interval if true.
139  pub fn contains_interval(&self, other: Interval) -> Option<Self> {
140    match self.constraint_interval(other) {
141      Some(interval) if other == interval => Some(interval),
142      _ => None,
143    }
144  }
145
146  /// Constraint this interval so that it contains the maximum overlapping region between
147  /// the two intervals. This will reduce the range of either interval so that the returned
148  /// interval contains start and end ranges valid to both intervals. No interval is returned
149  /// if there are no valid ranges.
150  pub fn constraint_interval(&self, other: Interval) -> Option<Self> {
151    let constrain = |self_bound, other_bound, is_start: bool| match (self_bound, other_bound) {
152      (None, None) => None,
153      (None, other_val @ Some(_)) => other_val,
154      (self_val @ Some(_), None) => self_val,
155      (Some(self_val), Some(other_val)) => {
156        if is_start {
157          Some(max(self_val, other_val))
158        } else {
159          Some(min(self_val, other_val))
160        }
161      }
162    };
163
164    let start = constrain(self.start, other.start, true);
165    let end = constrain(self.end, other.end, false);
166
167    let interval = Self::new(start, end);
168    if interval.is_valid() {
169      Some(interval)
170    } else {
171      None
172    }
173  }
174
175  /// Order the interval with another interval based on the range that either specify.
176  /// The interval with the larger range (end - start) is considered greater.
177  pub fn order_by_range(&self, other: &Interval) -> Ordering {
178    let self_range = self.end.unwrap_or(u32::MAX) - self.start.unwrap_or(u32::MIN);
179    let other_range = other.end.unwrap_or(u32::MAX) - other.start.unwrap_or(u32::MIN);
180
181    self_range.cmp(&other_range)
182  }
183
184  /// Is this a valid interval, i.e. is the start range less than the end range.
185  pub fn is_valid(&self) -> bool {
186    if let (Some(ref start), Some(ref end)) = (self.start, self.end)
187      && start >= end
188    {
189      return false;
190    }
191
192    true
193  }
194
195  /// Check if this interval contains the value.
196  pub fn contains(&self, value: u32) -> bool {
197    match (self.start.as_ref(), self.end.as_ref()) {
198      (None, None) => true,
199      (None, Some(end)) => value < *end,
200      (Some(start), None) => value >= *start,
201      (Some(start), Some(end)) => value >= *start && value < *end,
202    }
203  }
204
205  /// Convert this interval into a one-based noodles `Interval`.
206  #[instrument(level = "trace", skip_all, ret)]
207  pub fn into_one_based(self) -> io::Result<NoodlesInterval> {
208    Ok(match (self.start, self.end) {
209      (None, None) => NoodlesInterval::from(..),
210      (None, Some(end)) => NoodlesInterval::from(..=Self::convert_end(end)?),
211      (Some(start), None) => NoodlesInterval::from(Self::convert_start(start)?..),
212      (Some(start), Some(end)) => {
213        NoodlesInterval::from(Self::convert_start(start)?..=Self::convert_end(end)?)
214      }
215    })
216  }
217
218  /// Convert a start position to a noodles Position.
219  pub fn convert_start(start: u32) -> io::Result<Position> {
220    Self::convert_position(start, |value| {
221      value
222        .checked_add(1)
223        .ok_or_else(|| io::Error::other(format!("could not convert {value} to 1-based position.")))
224    })
225  }
226
227  /// Convert an end position to a noodles Position.
228  pub fn convert_end(end: u32) -> io::Result<Position> {
229    Self::convert_position(end, Ok)
230  }
231
232  /// Convert a u32 position to a noodles Position.
233  pub fn convert_position<F>(value: u32, convert_fn: F) -> io::Result<Position>
234  where
235    F: FnOnce(u32) -> io::Result<u32>,
236  {
237    let value = convert_fn(value).map(|value| {
238      usize::try_from(value)
239        .map_err(|err| io::Error::other(format!("could not convert `u32` to `usize`: {err}")))
240    })??;
241
242    Position::try_from(value).map_err(|err| {
243      io::Error::other(format!(
244        "could not convert `{value}` into `Position`: {err}"
245      ))
246    })
247  }
248
249  /// Start position.
250  pub fn start(&self) -> Option<u32> {
251    self.start
252  }
253
254  /// End position.
255  pub fn end(&self) -> Option<u32> {
256    self.end
257  }
258
259  /// Create a new interval
260  pub fn new(start: Option<u32>, end: Option<u32>) -> Self {
261    Self { start, end }
262  }
263}
264
265/// Schemes that can be used with htsget.
266#[derive(JsonSchema, Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
267#[serde(rename_all = "UPPERCASE", deny_unknown_fields)]
268pub enum Scheme {
269  #[default]
270  #[serde(alias = "Http", alias = "http")]
271  Http,
272  #[serde(alias = "Https", alias = "https")]
273  Https,
274}
275
276impl Display for Scheme {
277  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
278    match self {
279      Scheme::Http => write!(f, "http"),
280      Scheme::Https => write!(f, "https"),
281    }
282  }
283}
284
285/// Tagged Any allow type for cors config.
286#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
287#[serde(deny_unknown_fields)]
288pub enum TaggedTypeAll {
289  #[serde(alias = "all", alias = "ALL")]
290  All,
291}
292
293/// Possible values for the fields parameter.
294#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
295#[serde(untagged, deny_unknown_fields)]
296pub enum Fields {
297  /// Include all fields
298  Tagged(TaggedTypeAll),
299  /// List of fields to include
300  List(HashSet<String>),
301}
302
303impl Default for Fields {
304  fn default() -> Self {
305    Self::Tagged(TaggedTypeAll::All)
306  }
307}
308
309/// Possible values for the tags parameter.
310#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
311#[serde(untagged, deny_unknown_fields)]
312pub enum Tags {
313  /// Include all tags
314  Tagged(TaggedTypeAll),
315  /// List of tags to include
316  List(HashSet<String>),
317}
318
319impl Default for Tags {
320  fn default() -> Self {
321    Self::Tagged(TaggedTypeAll::All)
322  }
323}
324
325/// The no tags parameter.
326#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
327#[serde(deny_unknown_fields)]
328pub struct NoTags(pub Option<HashSet<String>>);
329
330/// A suppressed request has start/end intervals reduced because of authorization
331/// restrictions.
332#[derive(Clone, Debug, PartialEq, Eq, Default)]
333pub struct SuppressedRequest {
334  matching_rules: Vec<AuthorizationRule>,
335  constrained_interval: Option<Interval>,
336  empty_response: bool,
337  add_hint: bool,
338}
339
340impl SuppressedRequest {
341  /// Create a new suppressed request.
342  pub fn new(
343    matching_rules: Vec<AuthorizationRule>,
344    constrained_interval: Option<Interval>,
345    empty_response: bool,
346    add_hint: bool,
347  ) -> Self {
348    Self {
349      matching_rules,
350      constrained_interval,
351      empty_response,
352      add_hint,
353    }
354  }
355
356  /// Get the matching rules.
357  pub fn matching_rules(&self) -> &[AuthorizationRule] {
358    self.matching_rules.as_ref()
359  }
360
361  /// Take the matching rules out of this request.
362  pub fn take_matching_rules(&mut self) -> Vec<AuthorizationRule> {
363    self.matching_rules.drain(..).collect()
364  }
365
366  /// Get the constrained interval.
367  pub fn constrained_interval(&self) -> Option<&Interval> {
368    self.constrained_interval.as_ref()
369  }
370
371  /// Set the interval inside the query parameters.
372  pub fn set_constrained_interval(&mut self, interval: Interval) {
373    self.constrained_interval = Some(interval);
374  }
375
376  /// Set the matching rules.
377  pub fn set_matching_rules(&mut self, rules: Vec<AuthorizationRule>) {
378    self.matching_rules = rules;
379  }
380
381  /// Whether to return an empty response because nothing allows the user to see any
382  /// regions.
383  pub fn empty_response(&self) -> bool {
384    self.empty_response
385  }
386
387  /// Whether a hint should be added to the htsget response.
388  pub fn add_hint(&self) -> bool {
389    self.add_hint
390  }
391}
392
393/// A struct containing the information from the HTTP request.
394#[derive(Clone, Debug, PartialEq, Eq, Default)]
395pub struct Request {
396  path: String,
397  query: HashMap<String, String>,
398  headers: HeaderMap,
399}
400
401impl Request {
402  /// Create a new request.
403  pub fn new(id: String, query: HashMap<String, String>, headers: HeaderMap) -> Self {
404    Self {
405      path: id,
406      query,
407      headers,
408    }
409  }
410
411  /// Create a new request with default query and headers.
412  pub fn new_with_id(id: String) -> Self {
413    Self::new(id, Default::default(), Default::default())
414  }
415
416  /// Get the id.
417  pub fn path(&self) -> &str {
418    &self.path
419  }
420
421  /// Get the query.
422  pub fn query(&self) -> &HashMap<String, String> {
423    &self.query
424  }
425
426  /// Get the query as a mutable reference.
427  pub fn query_mut(&mut self) -> &mut HashMap<String, String> {
428    &mut self.query
429  }
430
431  /// Get the headers.
432  pub fn headers(&self) -> &HeaderMap {
433    &self.headers
434  }
435}
436
437/// A query contains all the parameters that can be used when requesting
438/// a search for either of `reads` or `variants`.
439#[derive(Clone, Debug, PartialEq, Eq, Default)]
440pub struct Query {
441  id: String,
442  format: Format,
443  class: Class,
444  /// Reference name
445  reference_name: Option<String>,
446  /// The start and end positions are 0-based. [start, end)
447  interval: Interval,
448  fields: Fields,
449  tags: Tags,
450  no_tags: NoTags,
451  /// The raw HTTP request information.
452  request: Request,
453  matching_rules: Option<Vec<AuthorizationRule>>,
454  #[cfg(feature = "experimental")]
455  encryption_scheme: Option<EncryptionScheme>,
456}
457
458impl Query {
459  /// Create a new query.
460  pub fn new(id: impl Into<String>, format: Format, request: Request) -> Self {
461    Self {
462      id: id.into(),
463      format,
464      request,
465      ..Default::default()
466    }
467  }
468
469  /// Create a new query with a default request.
470  pub fn new_with_default_request(id: impl Into<String>, format: Format) -> Self {
471    let id = id.into();
472    Self::new(id.clone(), format, Request::new_with_id(id))
473  }
474
475  /// Set the id.
476  pub fn set_id(&mut self, id: impl Into<String>) {
477    self.id = id.into();
478  }
479
480  /// Set the is and return self.
481  pub fn with_id(mut self, id: impl Into<String>) -> Self {
482    self.set_id(id);
483    self
484  }
485
486  /// Set the format.
487  pub fn with_format(mut self, format: Format) -> Self {
488    self.format = format;
489    self
490  }
491
492  /// Set the class.
493  pub fn with_class(mut self, class: Class) -> Self {
494    self.set_class(class);
495    self
496  }
497
498  /// Set the class.
499  pub fn set_class(&mut self, class: Class) {
500    self.class = class;
501  }
502
503  /// Set the reference name.
504  pub fn with_reference_name(mut self, reference_name: impl Into<String>) -> Self {
505    self.reference_name = Some(reference_name.into());
506    self
507  }
508
509  /// Set the interval.
510  pub fn with_start(mut self, start: u32) -> Self {
511    self.interval.start = Some(start);
512    self
513  }
514
515  /// Set the interval.
516  pub fn with_end(mut self, end: u32) -> Self {
517    self.interval.end = Some(end);
518    self
519  }
520
521  /// Set the interval.
522  pub fn with_interval(mut self, interval: Interval) -> Self {
523    self.set_interval(interval);
524    self
525  }
526
527  /// Set the interval.
528  pub fn set_interval(&mut self, interval: Interval) {
529    self.interval = interval;
530  }
531
532  /// Set the interval.
533  pub fn with_fields(mut self, fields: Fields) -> Self {
534    self.fields = fields;
535    self
536  }
537
538  /// Set the interval.
539  pub fn with_tags(mut self, tags: Tags) -> Self {
540    self.tags = tags;
541    self
542  }
543
544  /// Set no tags.
545  pub fn with_no_tags(mut self, no_tags: Vec<impl Into<String>>) -> Self {
546    self.no_tags = NoTags(Some(
547      no_tags.into_iter().map(|field| field.into()).collect(),
548    ));
549    self
550  }
551
552  /// Id.
553  pub fn id(&self) -> &str {
554    &self.id
555  }
556
557  /// Format.
558  pub fn format(&self) -> Format {
559    self.format
560  }
561
562  /// Class.
563  pub fn class(&self) -> Class {
564    self.class
565  }
566
567  /// Reference name.
568  pub fn reference_name(&self) -> Option<&str> {
569    self.reference_name.as_deref()
570  }
571
572  /// Interval.
573  pub fn interval(&self) -> Interval {
574    self.interval
575  }
576
577  /// Fields.
578  pub fn fields(&self) -> &Fields {
579    &self.fields
580  }
581
582  /// Tags.
583  pub fn tags(&self) -> &Tags {
584    &self.tags
585  }
586
587  /// No tags.
588  pub fn no_tags(&self) -> &NoTags {
589    &self.no_tags
590  }
591
592  /// Request.
593  pub fn request(&self) -> &Request {
594    &self.request
595  }
596
597  /// Set the matching rules.
598  pub fn set_matching_rules(&mut self, matching_rules: Vec<AuthorizationRule>) {
599    self.matching_rules = Some(matching_rules);
600  }
601
602  /// Set the encryption scheme.
603  #[cfg(feature = "experimental")]
604  pub fn with_encryption_scheme(mut self, encryption_scheme: EncryptionScheme) -> Self {
605    self.encryption_scheme = Some(encryption_scheme);
606    self
607  }
608
609  /// Get the encryption scheme
610  #[cfg(feature = "experimental")]
611  pub fn encryption_scheme(&self) -> Option<EncryptionScheme> {
612    self.encryption_scheme
613  }
614}
615
616/// Htsget specific errors.
617#[derive(Error, Debug, PartialEq, Eq)]
618pub enum HtsGetError {
619  #[error("not found: {0}")]
620  NotFound(String),
621
622  #[error("unsupported Format: {0}")]
623  UnsupportedFormat(String),
624
625  #[error("invalid input: {0}")]
626  InvalidInput(String),
627
628  #[error("invalid range: {0}")]
629  InvalidRange(String),
630
631  #[error("io error: {0}")]
632  IoError(String),
633
634  #[error("parsing error: {0}")]
635  ParseError(String),
636
637  #[error("internal error: {0}")]
638  InternalError(String),
639}
640
641impl HtsGetError {
642  /// Create a `NotFound` error.
643  pub fn not_found<S: Into<String>>(message: S) -> Self {
644    Self::NotFound(message.into())
645  }
646
647  /// Create an `UnsupportedFormat` error.
648  pub fn unsupported_format<S: Into<String>>(format: S) -> Self {
649    Self::UnsupportedFormat(format.into())
650  }
651
652  /// Create an `InvalidInput` error.
653  pub fn invalid_input<S: Into<String>>(message: S) -> Self {
654    Self::InvalidInput(message.into())
655  }
656
657  /// Create an `InvalidRange` error.
658  pub fn invalid_range<S: Into<String>>(message: S) -> Self {
659    Self::InvalidRange(message.into())
660  }
661
662  /// Create an `IoError` error.
663  pub fn io_error<S: Into<String>>(message: S) -> Self {
664    Self::IoError(message.into())
665  }
666
667  /// Create a `ParseError` error.
668  pub fn parse_error<S: Into<String>>(message: S) -> Self {
669    Self::ParseError(message.into())
670  }
671
672  /// Create an `InternalError` error.
673  pub fn internal_error<S: Into<String>>(message: S) -> Self {
674    Self::InternalError(message.into())
675  }
676}
677
678impl From<HtsGetError> for io::Error {
679  fn from(error: HtsGetError) -> Self {
680    Self::other(error)
681  }
682}
683
684impl From<io::Error> for HtsGetError {
685  fn from(err: io::Error) -> Self {
686    Self::io_error(err.to_string())
687  }
688}
689
690/// The headers that need to be supplied when requesting data from a url.
691#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
692#[serde(deny_unknown_fields)]
693pub struct Headers(HashMap<String, String>);
694
695impl Headers {
696  pub fn new(headers: HashMap<String, String>) -> Self {
697    Self(headers)
698  }
699
700  /// Insert an entry into the headers. If the entry already exists, the value will be appended to
701  /// the existing value, separated by a comma. Returns self.
702  pub fn with_header<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
703    self.insert(key, value);
704    self
705  }
706
707  pub fn is_empty(&self) -> bool {
708    self.0.is_empty()
709  }
710
711  /// Insert an entry into the headers. If the entry already exists, the value will be appended to
712  /// the existing value, separated by a comma.
713  pub fn insert<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
714    let entry = self.0.entry(key.into()).or_default();
715    if entry.is_empty() {
716      entry.push_str(&value.into());
717    } else {
718      entry.push_str(&format!(", {}", value.into()));
719    }
720  }
721
722  /// Add to the headers.
723  pub fn extend(&mut self, headers: Headers) {
724    self.0.extend(headers.into_inner());
725  }
726
727  /// Get the inner HashMap.
728  pub fn into_inner(self) -> HashMap<String, String> {
729    self.0
730  }
731
732  /// Get a reference to the inner HashMap.
733  pub fn as_ref_inner(&self) -> &HashMap<String, String> {
734    &self.0
735  }
736
737  /// Get a mutable reference to the inner HashMap.
738  pub fn as_mut_inner(&mut self) -> &mut HashMap<String, String> {
739    &mut self.0
740  }
741}
742
743impl TryFrom<&HeaderMap> for Headers {
744  type Error = Error;
745
746  fn try_from(headers: &HeaderMap) -> result::Result<Self, Self::Error> {
747    headers
748      .iter()
749      .try_fold(Headers::default(), |acc, (key, value)| {
750        Ok(acc.with_header(
751          key.to_string(),
752          value.to_str().map_err(|err| {
753            ParseError(format!("failed to convert header value to string: {err}"))
754          })?,
755        ))
756      })
757  }
758}
759
760/// A url from which raw data can be retrieved.
761#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
762#[serde(deny_unknown_fields)]
763pub struct Url {
764  pub url: String,
765  #[serde(skip_serializing_if = "Option::is_none")]
766  pub headers: Option<Headers>,
767  #[serde(skip_serializing_if = "Option::is_none")]
768  pub class: Option<Class>,
769}
770
771impl Url {
772  /// Create a new Url.
773  pub fn new<S: Into<String>>(url: S) -> Self {
774    Self {
775      url: url.into(),
776      headers: None,
777      class: None,
778    }
779  }
780
781  /// Add to the headers of the Url.
782  pub fn add_headers(mut self, headers: Headers) -> Self {
783    if !headers.is_empty() {
784      self
785        .headers
786        .get_or_insert_with(Headers::default)
787        .extend(headers);
788    }
789
790    self
791  }
792
793  /// Set the headers of the Url.
794  pub fn with_headers(mut self, headers: Headers) -> Self {
795    self.headers = Some(headers).filter(|h| !h.is_empty());
796    self
797  }
798
799  /// Set the class of the Url using an optional value.
800  pub fn set_class(mut self, class: Option<Class>) -> Self {
801    self.class = class;
802    self
803  }
804
805  /// Set the class of the Url.
806  pub fn with_class(self, class: Class) -> Self {
807    self.set_class(Some(class))
808  }
809}
810
811/// Wrapped json response for htsget.
812#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
813#[serde(deny_unknown_fields)]
814pub struct JsonResponse {
815  pub htsget: Response,
816}
817
818impl JsonResponse {
819  /// Create a new `JsonResponse`.
820  pub fn new(htsget: Response) -> Self {
821    Self { htsget }
822  }
823
824  #[cfg(feature = "experimental")]
825  /// Set the allowed restrictions.
826  pub fn with_allowed(mut self, allowed: Option<Vec<AuthorizationRule>>) -> Self {
827    self.htsget = self.htsget.with_allowed(allowed);
828    self
829  }
830}
831
832impl From<Response> for JsonResponse {
833  fn from(htsget: Response) -> Self {
834    Self::new(htsget)
835  }
836}
837
838/// The response for a HtsGet query.
839#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
840#[serde(deny_unknown_fields)]
841pub struct Response {
842  pub format: Format,
843  pub urls: Vec<Url>,
844  #[cfg(feature = "experimental")]
845  #[serde(skip_serializing_if = "Option::is_none")]
846  pub allowed: Option<Vec<AuthorizationRule>>,
847}
848
849impl Response {
850  /// Create a new `Response`.
851  pub fn new(format: Format, urls: Vec<Url>) -> Self {
852    Self {
853      format,
854      urls,
855      #[cfg(feature = "experimental")]
856      allowed: None,
857    }
858  }
859
860  #[cfg(feature = "experimental")]
861  /// Set the allowed restrictions.
862  pub fn with_allowed(mut self, allowed: Option<Vec<AuthorizationRule>>) -> Self {
863    self.allowed = allowed;
864    self
865  }
866}
867
868#[cfg(test)]
869mod tests {
870  use std::collections::{HashMap, HashSet};
871  use std::str::FromStr;
872
873  use http::{HeaderMap, HeaderName, HeaderValue};
874  use serde_json::{json, to_value};
875
876  use crate::types::{
877    Class, Fields, Format, Headers, HtsGetError, Interval, NoTags, Query, Response, TaggedTypeAll,
878    Tags, Url,
879  };
880
881  #[test]
882  fn interval_contains() {
883    let interval = Interval {
884      start: Some(0),
885      end: Some(10),
886    };
887    assert!(interval.contains(9));
888  }
889
890  #[test]
891  fn interval_not_contains() {
892    let interval = Interval {
893      start: Some(0),
894      end: Some(10),
895    };
896    assert!(!interval.contains(10));
897  }
898
899  #[test]
900  fn interval_contains_start_not_present() {
901    let interval = Interval {
902      start: None,
903      end: Some(10),
904    };
905    assert!(interval.contains(9));
906  }
907
908  #[test]
909  fn interval_not_contains_start_not_present() {
910    let interval = Interval {
911      start: None,
912      end: Some(10),
913    };
914    assert!(!interval.contains(10));
915  }
916
917  #[test]
918  fn interval_contains_end_not_present() {
919    let interval = Interval {
920      start: Some(1),
921      end: None,
922    };
923    assert!(interval.contains(9));
924  }
925
926  #[test]
927  fn interval_not_contains_end_not_present() {
928    let interval = Interval {
929      start: Some(1),
930      end: None,
931    };
932    assert!(!interval.contains(0));
933  }
934
935  #[test]
936  fn interval_contains_both_not_present() {
937    let interval = Interval {
938      start: None,
939      end: None,
940    };
941    assert!(interval.contains(0));
942  }
943
944  #[test]
945  fn interval_constrain_and_contains() {
946    let outer = Interval::new(Some(10), Some(20));
947    let inner = Interval::new(Some(12), Some(18));
948    assert_eq!(
949      outer.constraint_interval(inner),
950      Some(Interval::new(Some(12), Some(18)))
951    );
952    assert_eq!(
953      outer.contains_interval(inner),
954      Some(Interval::new(Some(12), Some(18)))
955    );
956
957    let outer = Interval::new(Some(10), Some(20));
958    let inner = Interval::new(Some(10), Some(20));
959    assert_eq!(
960      outer.constraint_interval(inner),
961      Some(Interval::new(Some(10), Some(20)))
962    );
963    assert_eq!(
964      outer.contains_interval(inner),
965      Some(Interval::new(Some(10), Some(20)))
966    );
967
968    let outer = Interval::new(Some(10), Some(20));
969    let inner = Interval::new(Some(5), Some(15));
970    assert_eq!(
971      outer.constraint_interval(inner),
972      Some(Interval::new(Some(10), Some(15)))
973    );
974    assert_eq!(outer.contains_interval(inner), None);
975
976    let outer = Interval::new(Some(10), Some(20));
977    let inner = Interval::new(Some(15), Some(25));
978    assert_eq!(
979      outer.constraint_interval(inner),
980      Some(Interval::new(Some(15), Some(20)))
981    );
982    assert_eq!(outer.contains_interval(inner), None);
983
984    let outer = Interval::new(None, Some(20));
985    let inner = Interval::new(Some(10), Some(15));
986    assert_eq!(
987      outer.constraint_interval(inner),
988      Some(Interval::new(Some(10), Some(15)))
989    );
990    assert_eq!(
991      outer.contains_interval(inner),
992      Some(Interval::new(Some(10), Some(15)))
993    );
994
995    let outer = Interval::new(Some(10), None);
996    let inner = Interval::new(Some(15), Some(25));
997    assert_eq!(
998      outer.constraint_interval(inner),
999      Some(Interval::new(Some(15), Some(25)))
1000    );
1001    assert_eq!(
1002      outer.contains_interval(inner),
1003      Some(Interval::new(Some(15), Some(25)))
1004    );
1005
1006    let outer = Interval::new(None, None);
1007    let inner = Interval::new(Some(10), Some(20));
1008    assert_eq!(
1009      outer.constraint_interval(inner),
1010      Some(Interval::new(Some(10), Some(20)))
1011    );
1012    assert_eq!(
1013      outer.contains_interval(inner),
1014      Some(Interval::new(Some(10), Some(20)))
1015    );
1016
1017    let outer = Interval::new(Some(10), Some(20));
1018    let inner = Interval::new(None, Some(15));
1019    assert_eq!(
1020      outer.constraint_interval(inner),
1021      Some(Interval::new(Some(10), Some(15)))
1022    );
1023    assert_eq!(outer.contains_interval(inner), None);
1024
1025    let outer = Interval::new(Some(10), Some(20));
1026    let inner = Interval::new(Some(15), None);
1027    assert_eq!(
1028      outer.constraint_interval(inner),
1029      Some(Interval::new(Some(15), Some(20)))
1030    );
1031    assert_eq!(outer.contains_interval(inner), None);
1032
1033    let outer = Interval::new(None, None);
1034    let inner = Interval::new(None, None);
1035    assert_eq!(
1036      outer.constraint_interval(inner),
1037      Some(Interval::new(None, None))
1038    );
1039    assert_eq!(
1040      outer.contains_interval(inner),
1041      Some(Interval::new(None, None))
1042    );
1043  }
1044
1045  #[test]
1046  fn htsget_error_not_found() {
1047    let result = HtsGetError::not_found("error");
1048    assert!(matches!(result, HtsGetError::NotFound(message) if message == "error"));
1049  }
1050
1051  #[test]
1052  fn htsget_error_unsupported_format() {
1053    let result = HtsGetError::unsupported_format("error");
1054    assert!(matches!(result, HtsGetError::UnsupportedFormat(message) if message == "error"));
1055  }
1056
1057  #[test]
1058  fn htsget_error_invalid_input() {
1059    let result = HtsGetError::invalid_input("error");
1060    assert!(matches!(result, HtsGetError::InvalidInput(message) if message == "error"));
1061  }
1062
1063  #[test]
1064  fn htsget_error_invalid_range() {
1065    let result = HtsGetError::invalid_range("error");
1066    assert!(matches!(result, HtsGetError::InvalidRange(message) if message == "error"));
1067  }
1068
1069  #[test]
1070  fn htsget_error_io_error() {
1071    let result = HtsGetError::io_error("error");
1072    assert!(matches!(result, HtsGetError::IoError(message) if message == "error"));
1073  }
1074
1075  #[test]
1076  fn htsget_error_parse_error() {
1077    let result = HtsGetError::parse_error("error");
1078    assert!(matches!(result, HtsGetError::ParseError(message) if message == "error"));
1079  }
1080
1081  #[test]
1082  fn htsget_error_internal_error() {
1083    let result = HtsGetError::internal_error("error");
1084    assert!(matches!(result, HtsGetError::InternalError(message) if message == "error"));
1085  }
1086
1087  #[test]
1088  fn query_new() {
1089    let result = Query::new_with_default_request("NA12878", Format::Bam);
1090    assert_eq!(result.id(), "NA12878");
1091  }
1092
1093  #[test]
1094  fn query_with_format() {
1095    let result = Query::new_with_default_request("NA12878", Format::Bam);
1096    assert_eq!(result.format(), Format::Bam);
1097  }
1098
1099  #[test]
1100  fn query_with_class() {
1101    let result = Query::new_with_default_request("NA12878", Format::Bam).with_class(Class::Header);
1102    assert_eq!(result.class(), Class::Header);
1103  }
1104
1105  #[test]
1106  fn query_with_reference_name() {
1107    let result =
1108      Query::new_with_default_request("NA12878", Format::Bam).with_reference_name("chr1");
1109    assert_eq!(result.reference_name(), Some("chr1"));
1110  }
1111
1112  #[test]
1113  fn query_with_start() {
1114    let result = Query::new_with_default_request("NA12878", Format::Bam).with_start(0);
1115    assert_eq!(result.interval().start(), Some(0));
1116  }
1117
1118  #[test]
1119  fn query_with_end() {
1120    let result = Query::new_with_default_request("NA12878", Format::Bam).with_end(0);
1121    assert_eq!(result.interval().end(), Some(0));
1122  }
1123
1124  #[test]
1125  fn query_with_fields() {
1126    let result = Query::new_with_default_request("NA12878", Format::Bam).with_fields(Fields::List(
1127      HashSet::from_iter(vec!["QNAME".to_string(), "FLAG".to_string()]),
1128    ));
1129    assert_eq!(
1130      result.fields(),
1131      &Fields::List(HashSet::from_iter(vec![
1132        "QNAME".to_string(),
1133        "FLAG".to_string()
1134      ]))
1135    );
1136  }
1137
1138  #[test]
1139  fn query_with_tags() {
1140    let result = Query::new_with_default_request("NA12878", Format::Bam)
1141      .with_tags(Tags::Tagged(TaggedTypeAll::All));
1142    assert_eq!(result.tags(), &Tags::Tagged(TaggedTypeAll::All));
1143  }
1144
1145  #[test]
1146  fn query_with_no_tags() {
1147    let result =
1148      Query::new_with_default_request("NA12878", Format::Bam).with_no_tags(vec!["RG", "OQ"]);
1149    assert_eq!(
1150      result.no_tags(),
1151      &NoTags(Some(HashSet::from_iter(vec![
1152        "RG".to_string(),
1153        "OQ".to_string()
1154      ])))
1155    );
1156  }
1157
1158  #[test]
1159  fn format_from_bam() {
1160    let result = String::from(Format::Bam);
1161    assert_eq!(result, "BAM");
1162  }
1163
1164  #[test]
1165  fn format_from_cram() {
1166    let result = String::from(Format::Cram);
1167    assert_eq!(result, "CRAM");
1168  }
1169
1170  #[test]
1171  fn format_from_vcf() {
1172    let result = String::from(Format::Vcf);
1173    assert_eq!(result, "VCF");
1174  }
1175
1176  #[test]
1177  fn format_from_bcf() {
1178    let result = String::from(Format::Bcf);
1179    assert_eq!(result, "BCF");
1180  }
1181
1182  #[test]
1183  fn headers_with_header() {
1184    let header = Headers::new(HashMap::new()).with_header("Range", "bytes=0-1023");
1185    let result = header.0.get("Range");
1186    assert_eq!(result, Some(&"bytes=0-1023".to_string()));
1187  }
1188
1189  #[test]
1190  fn headers_is_empty() {
1191    assert!(Headers::new(HashMap::new()).is_empty());
1192  }
1193
1194  #[test]
1195  fn headers_insert() {
1196    let mut header = Headers::new(HashMap::new());
1197    header.insert("Range", "bytes=0-1023");
1198    let result = header.0.get("Range");
1199    assert_eq!(result, Some(&"bytes=0-1023".to_string()));
1200  }
1201
1202  #[test]
1203  fn headers_extend() {
1204    let mut headers = Headers::new(HashMap::new());
1205    headers.insert("Range", "bytes=0-1023");
1206
1207    let mut extend_with = Headers::new(HashMap::new());
1208    extend_with.insert("header", "value");
1209
1210    headers.extend(extend_with);
1211
1212    let result = headers.0.get("Range");
1213    assert_eq!(result, Some(&"bytes=0-1023".to_string()));
1214
1215    let result = headers.0.get("header");
1216    assert_eq!(result, Some(&"value".to_string()));
1217  }
1218
1219  #[test]
1220  fn headers_multiple_values() {
1221    let headers = Headers::new(HashMap::new())
1222      .with_header("Range", "bytes=0-1023")
1223      .with_header("Range", "bytes=1024-2047");
1224    let result = headers.0.get("Range");
1225
1226    assert_eq!(result, Some(&"bytes=0-1023, bytes=1024-2047".to_string()));
1227  }
1228
1229  #[test]
1230  fn headers_try_from_header_map() {
1231    let mut headers = HeaderMap::new();
1232    headers.append(
1233      HeaderName::from_str("Range").unwrap(),
1234      HeaderValue::from_str("bytes=0-1023").unwrap(),
1235    );
1236    headers.append(
1237      HeaderName::from_str("Range").unwrap(),
1238      HeaderValue::from_str("bytes=1024-2047").unwrap(),
1239    );
1240    headers.append(
1241      HeaderName::from_str("Range").unwrap(),
1242      HeaderValue::from_str("bytes=2048-3071, bytes=3072-4095").unwrap(),
1243    );
1244    let headers: Headers = (&headers).try_into().unwrap();
1245
1246    let result = headers.0.get("range");
1247    assert_eq!(
1248      result,
1249      Some(&"bytes=0-1023, bytes=1024-2047, bytes=2048-3071, bytes=3072-4095".to_string())
1250    );
1251  }
1252
1253  #[test]
1254  fn serialize_headers() {
1255    let headers = Headers::new(HashMap::new())
1256      .with_header("Range", "bytes=0-1023")
1257      .with_header("Range", "bytes=1024-2047");
1258
1259    let result = to_value(headers).unwrap();
1260    assert_eq!(
1261      result,
1262      json!({
1263        "Range" : "bytes=0-1023, bytes=1024-2047"
1264      })
1265    );
1266  }
1267
1268  #[test]
1269  fn url_with_headers() {
1270    let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")
1271      .with_headers(Headers::new(HashMap::new()));
1272    assert_eq!(result.headers, None);
1273  }
1274
1275  #[test]
1276  fn url_add_headers() {
1277    let mut headers = Headers::new(HashMap::new());
1278    headers.insert("Range", "bytes=0-1023");
1279
1280    let mut extend_with = Headers::new(HashMap::new());
1281    extend_with.insert("header", "value");
1282
1283    let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")
1284      .with_headers(headers)
1285      .add_headers(extend_with);
1286
1287    let expected_headers = Headers::new(HashMap::new())
1288      .with_header("Range", "bytes=0-1023")
1289      .with_header("header", "value");
1290
1291    assert_eq!(result.headers, Some(expected_headers));
1292  }
1293
1294  #[test]
1295  fn url_with_class() {
1296    let result =
1297      Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==").with_class(Class::Header);
1298    assert_eq!(result.class, Some(Class::Header));
1299  }
1300
1301  #[test]
1302  fn url_set_class() {
1303    let result =
1304      Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==").set_class(Some(Class::Header));
1305    assert_eq!(result.class, Some(Class::Header));
1306  }
1307
1308  #[test]
1309  fn url_new() {
1310    let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==");
1311    assert_eq!(result.url, "data:application/vnd.ga4gh.bam;base64,QkFNAQ==");
1312    assert_eq!(result.headers, None);
1313    assert_eq!(result.class, None);
1314  }
1315
1316  #[test]
1317  fn response_new() {
1318    let result = Response::new(
1319      Format::Bam,
1320      vec![Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")],
1321    );
1322    assert_eq!(result.format, Format::Bam);
1323    assert_eq!(
1324      result.urls,
1325      vec![Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")]
1326    );
1327  }
1328}