1use std::collections::{HashMap, HashSet};
5use std::fmt::{Debug, Display, Formatter};
6use std::{fmt, io, result};
7
8#[cfg(feature = "experimental")]
9use crate::encryption_scheme::EncryptionScheme;
10use crate::error::Error;
11use crate::error::Error::ParseError;
12use http::HeaderMap;
13use noodles::core::Position;
14use noodles::core::region::Interval as NoodlesInterval;
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use thiserror::Error;
18use tracing::instrument;
19
20pub type Result<T> = result::Result<T, HtsGetError>;
22
23#[derive(JsonSchema, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
25#[serde(rename_all(serialize = "UPPERCASE"), deny_unknown_fields)]
26pub enum Format {
27 #[default]
28 #[serde(alias = "bam", alias = "BAM")]
29 Bam,
30 #[serde(alias = "cram", alias = "CRAM")]
31 Cram,
32 #[serde(alias = "vcf", alias = "VCF")]
33 Vcf,
34 #[serde(alias = "bcf", alias = "BCF")]
35 Bcf,
36}
37
38impl Format {
40 pub fn file_ending(&self) -> &str {
42 match self {
43 Format::Bam => ".bam",
44 Format::Cram => ".cram",
45 Format::Vcf => ".vcf.gz",
46 Format::Bcf => ".bcf",
47 }
48 }
49
50 pub fn fmt_file(&self, id: &str) -> String {
52 format!("{id}{}", self.file_ending())
53 }
54
55 pub fn index_file_ending(&self) -> &str {
57 match self {
58 Format::Bam => ".bam.bai",
59 Format::Cram => ".cram.crai",
60 Format::Vcf => ".vcf.gz.tbi",
61 Format::Bcf => ".bcf.csi",
62 }
63 }
64
65 pub fn fmt_index(&self, id: &str) -> String {
67 format!("{id}{}", self.index_file_ending())
68 }
69
70 pub fn gzi_index_file_ending(&self) -> io::Result<&str> {
72 match self {
73 Format::Bam => Ok(".bam.gzi"),
74 Format::Cram => Err(io::Error::other("CRAM does not support GZI".to_string())),
75 Format::Vcf => Ok(".vcf.gz.gzi"),
76 Format::Bcf => Ok(".bcf.gzi"),
77 }
78 }
79
80 pub fn fmt_gzi(&self, id: &str) -> io::Result<String> {
82 Ok(format!("{id}{}", self.gzi_index_file_ending()?))
83 }
84
85 pub fn is_index(id: &str) -> bool {
87 id.ends_with(".bai")
88 || id.ends_with(".crai")
89 || id.ends_with(".tbi")
90 || id.ends_with(".csi")
91 || id.ends_with(".gzi")
92 }
93}
94
95impl From<Format> for String {
96 fn from(format: Format) -> Self {
97 format.to_string()
98 }
99}
100
101impl Display for Format {
102 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103 match self {
104 Format::Bam => write!(f, "BAM"),
105 Format::Cram => write!(f, "CRAM"),
106 Format::Vcf => write!(f, "VCF"),
107 Format::Bcf => write!(f, "BCF"),
108 }
109 }
110}
111
112#[derive(Copy, Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
114#[serde(rename_all(serialize = "lowercase"), deny_unknown_fields)]
115pub enum Class {
116 #[serde(alias = "header", alias = "HEADER")]
117 Header,
118 #[default]
119 #[serde(alias = "body", alias = "BODY")]
120 Body,
121}
122
123#[derive(JsonSchema, Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
126#[serde(deny_unknown_fields)]
127pub struct Interval {
128 #[serde(skip_serializing_if = "Option::is_none")]
130 start: Option<u32>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 end: Option<u32>,
134}
135
136impl Interval {
137 pub fn contains_interval(&self, other: Interval) -> bool {
139 let check_containment =
140 |self_bound, other_bound, is_start: bool| match (self_bound, other_bound) {
141 (None, _) => true,
142 (Some(_), None) => false,
143 (Some(self_val), Some(other_val)) => {
144 if is_start {
145 self_val <= other_val
146 } else {
147 self_val >= other_val
148 }
149 }
150 };
151
152 let start_contains = check_containment(self.start, other.start, true);
153 let end_contains = check_containment(self.end, other.end, false);
154
155 start_contains && end_contains
156 }
157
158 pub fn contains(&self, value: u32) -> bool {
160 match (self.start.as_ref(), self.end.as_ref()) {
161 (None, None) => true,
162 (None, Some(end)) => value < *end,
163 (Some(start), None) => value >= *start,
164 (Some(start), Some(end)) => value >= *start && value < *end,
165 }
166 }
167
168 #[instrument(level = "trace", skip_all, ret)]
170 pub fn into_one_based(self) -> io::Result<NoodlesInterval> {
171 Ok(match (self.start, self.end) {
172 (None, None) => NoodlesInterval::from(..),
173 (None, Some(end)) => NoodlesInterval::from(..=Self::convert_end(end)?),
174 (Some(start), None) => NoodlesInterval::from(Self::convert_start(start)?..),
175 (Some(start), Some(end)) => {
176 NoodlesInterval::from(Self::convert_start(start)?..=Self::convert_end(end)?)
177 }
178 })
179 }
180
181 pub fn convert_start(start: u32) -> io::Result<Position> {
183 Self::convert_position(start, |value| {
184 value
185 .checked_add(1)
186 .ok_or_else(|| io::Error::other(format!("could not convert {value} to 1-based position.")))
187 })
188 }
189
190 pub fn convert_end(end: u32) -> io::Result<Position> {
192 Self::convert_position(end, Ok)
193 }
194
195 pub fn convert_position<F>(value: u32, convert_fn: F) -> io::Result<Position>
197 where
198 F: FnOnce(u32) -> io::Result<u32>,
199 {
200 let value = convert_fn(value).map(|value| {
201 usize::try_from(value)
202 .map_err(|err| io::Error::other(format!("could not convert `u32` to `usize`: {err}")))
203 })??;
204
205 Position::try_from(value).map_err(|err| {
206 io::Error::other(format!(
207 "could not convert `{value}` into `Position`: {err}"
208 ))
209 })
210 }
211
212 pub fn start(&self) -> Option<u32> {
214 self.start
215 }
216
217 pub fn end(&self) -> Option<u32> {
219 self.end
220 }
221
222 pub fn new(start: Option<u32>, end: Option<u32>) -> Self {
224 Self { start, end }
225 }
226}
227
228#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
230#[serde(rename_all = "UPPERCASE", deny_unknown_fields)]
231pub enum Scheme {
232 #[default]
233 #[serde(alias = "Http", alias = "http")]
234 Http,
235 #[serde(alias = "Https", alias = "https")]
236 Https,
237}
238
239impl Display for Scheme {
240 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
241 match self {
242 Scheme::Http => write!(f, "http"),
243 Scheme::Https => write!(f, "https"),
244 }
245 }
246}
247
248#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
250#[serde(deny_unknown_fields)]
251pub enum TaggedTypeAll {
252 #[serde(alias = "all", alias = "ALL")]
253 All,
254}
255
256#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
258#[serde(untagged, deny_unknown_fields)]
259pub enum Fields {
260 Tagged(TaggedTypeAll),
262 List(HashSet<String>),
264}
265
266impl Default for Fields {
267 fn default() -> Self {
268 Self::Tagged(TaggedTypeAll::All)
269 }
270}
271
272#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
274#[serde(untagged, deny_unknown_fields)]
275pub enum Tags {
276 Tagged(TaggedTypeAll),
278 List(HashSet<String>),
280}
281
282impl Default for Tags {
283 fn default() -> Self {
284 Self::Tagged(TaggedTypeAll::All)
285 }
286}
287
288#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
290#[serde(deny_unknown_fields)]
291pub struct NoTags(pub Option<HashSet<String>>);
292
293#[derive(Clone, Debug, PartialEq, Eq, Default)]
295pub struct Request {
296 path: String,
297 query: HashMap<String, String>,
298 headers: HeaderMap,
299}
300
301impl Request {
302 pub fn new(id: String, query: HashMap<String, String>, headers: HeaderMap) -> Self {
304 Self {
305 path: id,
306 query,
307 headers,
308 }
309 }
310
311 pub fn new_with_id(id: String) -> Self {
313 Self::new(id, Default::default(), Default::default())
314 }
315
316 pub fn path(&self) -> &str {
318 &self.path
319 }
320
321 pub fn query(&self) -> &HashMap<String, String> {
323 &self.query
324 }
325
326 pub fn headers(&self) -> &HeaderMap {
328 &self.headers
329 }
330}
331
332#[derive(Clone, Debug, PartialEq, Eq, Default)]
335pub struct Query {
336 id: String,
337 format: Format,
338 class: Class,
339 reference_name: Option<String>,
341 interval: Interval,
343 fields: Fields,
344 tags: Tags,
345 no_tags: NoTags,
346 request: Request,
348 #[cfg(feature = "experimental")]
349 encryption_scheme: Option<EncryptionScheme>,
350}
351
352impl Query {
353 pub fn new(id: impl Into<String>, format: Format, request: Request) -> Self {
355 Self {
356 id: id.into(),
357 format,
358 request,
359 ..Default::default()
360 }
361 }
362
363 pub fn new_with_default_request(id: impl Into<String>, format: Format) -> Self {
365 let id = id.into();
366 Self::new(id.clone(), format, Request::new_with_id(id))
367 }
368
369 pub fn set_id(&mut self, id: impl Into<String>) {
371 self.id = id.into();
372 }
373
374 pub fn with_id(mut self, id: impl Into<String>) -> Self {
376 self.set_id(id);
377 self
378 }
379
380 pub fn with_format(mut self, format: Format) -> Self {
382 self.format = format;
383 self
384 }
385
386 pub fn with_class(mut self, class: Class) -> Self {
388 self.class = class;
389 self
390 }
391
392 pub fn with_reference_name(mut self, reference_name: impl Into<String>) -> Self {
394 self.reference_name = Some(reference_name.into());
395 self
396 }
397
398 pub fn with_start(mut self, start: u32) -> Self {
400 self.interval.start = Some(start);
401 self
402 }
403
404 pub fn with_end(mut self, end: u32) -> Self {
406 self.interval.end = Some(end);
407 self
408 }
409
410 pub fn with_fields(mut self, fields: Fields) -> Self {
412 self.fields = fields;
413 self
414 }
415
416 pub fn with_tags(mut self, tags: Tags) -> Self {
418 self.tags = tags;
419 self
420 }
421
422 pub fn with_no_tags(mut self, no_tags: Vec<impl Into<String>>) -> Self {
424 self.no_tags = NoTags(Some(
425 no_tags.into_iter().map(|field| field.into()).collect(),
426 ));
427 self
428 }
429
430 pub fn id(&self) -> &str {
432 &self.id
433 }
434
435 pub fn format(&self) -> Format {
437 self.format
438 }
439
440 pub fn class(&self) -> Class {
442 self.class
443 }
444
445 pub fn reference_name(&self) -> Option<&str> {
447 self.reference_name.as_deref()
448 }
449
450 pub fn interval(&self) -> Interval {
452 self.interval
453 }
454
455 pub fn fields(&self) -> &Fields {
457 &self.fields
458 }
459
460 pub fn tags(&self) -> &Tags {
462 &self.tags
463 }
464
465 pub fn no_tags(&self) -> &NoTags {
467 &self.no_tags
468 }
469
470 pub fn request(&self) -> &Request {
472 &self.request
473 }
474
475 #[cfg(feature = "experimental")]
477 pub fn with_encryption_scheme(mut self, encryption_scheme: EncryptionScheme) -> Self {
478 self.encryption_scheme = Some(encryption_scheme);
479 self
480 }
481
482 #[cfg(feature = "experimental")]
484 pub fn encryption_scheme(&self) -> Option<EncryptionScheme> {
485 self.encryption_scheme
486 }
487}
488
489#[derive(Error, Debug, PartialEq, Eq)]
491pub enum HtsGetError {
492 #[error("not found: {0}")]
493 NotFound(String),
494
495 #[error("unsupported Format: {0}")]
496 UnsupportedFormat(String),
497
498 #[error("invalid input: {0}")]
499 InvalidInput(String),
500
501 #[error("invalid range: {0}")]
502 InvalidRange(String),
503
504 #[error("io error: {0}")]
505 IoError(String),
506
507 #[error("parsing error: {0}")]
508 ParseError(String),
509
510 #[error("internal error: {0}")]
511 InternalError(String),
512}
513
514impl HtsGetError {
515 pub fn not_found<S: Into<String>>(message: S) -> Self {
517 Self::NotFound(message.into())
518 }
519
520 pub fn unsupported_format<S: Into<String>>(format: S) -> Self {
522 Self::UnsupportedFormat(format.into())
523 }
524
525 pub fn invalid_input<S: Into<String>>(message: S) -> Self {
527 Self::InvalidInput(message.into())
528 }
529
530 pub fn invalid_range<S: Into<String>>(message: S) -> Self {
532 Self::InvalidRange(message.into())
533 }
534
535 pub fn io_error<S: Into<String>>(message: S) -> Self {
537 Self::IoError(message.into())
538 }
539
540 pub fn parse_error<S: Into<String>>(message: S) -> Self {
542 Self::ParseError(message.into())
543 }
544
545 pub fn internal_error<S: Into<String>>(message: S) -> Self {
547 Self::InternalError(message.into())
548 }
549}
550
551impl From<HtsGetError> for io::Error {
552 fn from(error: HtsGetError) -> Self {
553 Self::other(error)
554 }
555}
556
557impl From<io::Error> for HtsGetError {
558 fn from(err: io::Error) -> Self {
559 Self::io_error(err.to_string())
560 }
561}
562
563#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
565#[serde(deny_unknown_fields)]
566pub struct Headers(HashMap<String, String>);
567
568impl Headers {
569 pub fn new(headers: HashMap<String, String>) -> Self {
570 Self(headers)
571 }
572
573 pub fn with_header<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
576 self.insert(key, value);
577 self
578 }
579
580 pub fn is_empty(&self) -> bool {
581 self.0.is_empty()
582 }
583
584 pub fn insert<K: Into<String>, V: Into<String>>(&mut self, key: K, value: V) {
587 let entry = self.0.entry(key.into()).or_default();
588 if entry.is_empty() {
589 entry.push_str(&value.into());
590 } else {
591 entry.push_str(&format!(", {}", value.into()));
592 }
593 }
594
595 pub fn extend(&mut self, headers: Headers) {
597 self.0.extend(headers.into_inner());
598 }
599
600 pub fn into_inner(self) -> HashMap<String, String> {
602 self.0
603 }
604
605 pub fn as_ref_inner(&self) -> &HashMap<String, String> {
607 &self.0
608 }
609
610 pub fn as_mut_inner(&mut self) -> &mut HashMap<String, String> {
612 &mut self.0
613 }
614}
615
616impl TryFrom<&HeaderMap> for Headers {
617 type Error = Error;
618
619 fn try_from(headers: &HeaderMap) -> result::Result<Self, Self::Error> {
620 headers
621 .iter()
622 .try_fold(Headers::default(), |acc, (key, value)| {
623 Ok(acc.with_header(
624 key.to_string(),
625 value.to_str().map_err(|err| {
626 ParseError(format!("failed to convert header value to string: {err}"))
627 })?,
628 ))
629 })
630 }
631}
632
633#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
635#[serde(deny_unknown_fields)]
636pub struct Url {
637 pub url: String,
638 #[serde(skip_serializing_if = "Option::is_none")]
639 pub headers: Option<Headers>,
640 #[serde(skip_serializing_if = "Option::is_none")]
641 pub class: Option<Class>,
642}
643
644impl Url {
645 pub fn new<S: Into<String>>(url: S) -> Self {
647 Self {
648 url: url.into(),
649 headers: None,
650 class: None,
651 }
652 }
653
654 pub fn add_headers(mut self, headers: Headers) -> Self {
656 if !headers.is_empty() {
657 self
658 .headers
659 .get_or_insert_with(Headers::default)
660 .extend(headers);
661 }
662
663 self
664 }
665
666 pub fn with_headers(mut self, headers: Headers) -> Self {
668 self.headers = Some(headers).filter(|h| !h.is_empty());
669 self
670 }
671
672 pub fn set_class(mut self, class: Option<Class>) -> Self {
674 self.class = class;
675 self
676 }
677
678 pub fn with_class(self, class: Class) -> Self {
680 self.set_class(Some(class))
681 }
682}
683
684#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
686#[serde(deny_unknown_fields)]
687pub struct JsonResponse {
688 pub htsget: Response,
689}
690
691impl JsonResponse {
692 pub fn new(htsget: Response) -> Self {
694 Self { htsget }
695 }
696}
697
698impl From<Response> for JsonResponse {
699 fn from(htsget: Response) -> Self {
700 Self::new(htsget)
701 }
702}
703
704#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
706#[serde(deny_unknown_fields)]
707pub struct Response {
708 pub format: Format,
709 pub urls: Vec<Url>,
710}
711
712impl Response {
713 pub fn new(format: Format, urls: Vec<Url>) -> Self {
715 Self { format, urls }
716 }
717}
718
719#[cfg(test)]
720mod tests {
721 use std::collections::{HashMap, HashSet};
722 use std::str::FromStr;
723
724 use http::{HeaderMap, HeaderName, HeaderValue};
725 use serde_json::{json, to_value};
726
727 use crate::types::{
728 Class, Fields, Format, Headers, HtsGetError, Interval, NoTags, Query, Response, TaggedTypeAll,
729 Tags, Url,
730 };
731
732 #[test]
733 fn interval_contains() {
734 let interval = Interval {
735 start: Some(0),
736 end: Some(10),
737 };
738 assert!(interval.contains(9));
739 }
740
741 #[test]
742 fn interval_not_contains() {
743 let interval = Interval {
744 start: Some(0),
745 end: Some(10),
746 };
747 assert!(!interval.contains(10));
748 }
749
750 #[test]
751 fn interval_contains_start_not_present() {
752 let interval = Interval {
753 start: None,
754 end: Some(10),
755 };
756 assert!(interval.contains(9));
757 }
758
759 #[test]
760 fn interval_not_contains_start_not_present() {
761 let interval = Interval {
762 start: None,
763 end: Some(10),
764 };
765 assert!(!interval.contains(10));
766 }
767
768 #[test]
769 fn interval_contains_end_not_present() {
770 let interval = Interval {
771 start: Some(1),
772 end: None,
773 };
774 assert!(interval.contains(9));
775 }
776
777 #[test]
778 fn interval_not_contains_end_not_present() {
779 let interval = Interval {
780 start: Some(1),
781 end: None,
782 };
783 assert!(!interval.contains(0));
784 }
785
786 #[test]
787 fn interval_contains_both_not_present() {
788 let interval = Interval {
789 start: None,
790 end: None,
791 };
792 assert!(interval.contains(0));
793 }
794
795 #[test]
796 fn interval_contains_interval() {
797 let outer = Interval::new(Some(10), Some(20));
798 let inner = Interval::new(Some(12), Some(18));
799 assert!(outer.contains_interval(inner));
800
801 let outer = Interval::new(Some(10), Some(20));
802 let inner = Interval::new(Some(10), Some(20));
803 assert!(outer.contains_interval(inner));
804
805 let outer = Interval::new(Some(10), Some(20));
806 let inner = Interval::new(Some(5), Some(15));
807 assert!(!outer.contains_interval(inner));
808
809 let outer = Interval::new(Some(10), Some(20));
810 let inner = Interval::new(Some(15), Some(25));
811 assert!(!outer.contains_interval(inner));
812
813 let outer = Interval::new(None, Some(20));
814 let inner = Interval::new(Some(10), Some(15));
815 assert!(outer.contains_interval(inner));
816
817 let outer = Interval::new(Some(10), None);
818 let inner = Interval::new(Some(15), Some(25));
819 assert!(outer.contains_interval(inner));
820
821 let outer = Interval::new(None, None);
822 let inner = Interval::new(Some(10), Some(20));
823 assert!(outer.contains_interval(inner));
824
825 let outer = Interval::new(Some(10), Some(20));
826 let inner = Interval::new(None, Some(15));
827 assert!(!outer.contains_interval(inner));
828
829 let outer = Interval::new(Some(10), Some(20));
830 let inner = Interval::new(Some(15), None);
831 assert!(!outer.contains_interval(inner));
832
833 let outer = Interval::new(None, None);
834 let inner = Interval::new(None, None);
835 assert!(outer.contains_interval(inner));
836 }
837
838 #[test]
839 fn htsget_error_not_found() {
840 let result = HtsGetError::not_found("error");
841 assert!(matches!(result, HtsGetError::NotFound(message) if message == "error"));
842 }
843
844 #[test]
845 fn htsget_error_unsupported_format() {
846 let result = HtsGetError::unsupported_format("error");
847 assert!(matches!(result, HtsGetError::UnsupportedFormat(message) if message == "error"));
848 }
849
850 #[test]
851 fn htsget_error_invalid_input() {
852 let result = HtsGetError::invalid_input("error");
853 assert!(matches!(result, HtsGetError::InvalidInput(message) if message == "error"));
854 }
855
856 #[test]
857 fn htsget_error_invalid_range() {
858 let result = HtsGetError::invalid_range("error");
859 assert!(matches!(result, HtsGetError::InvalidRange(message) if message == "error"));
860 }
861
862 #[test]
863 fn htsget_error_io_error() {
864 let result = HtsGetError::io_error("error");
865 assert!(matches!(result, HtsGetError::IoError(message) if message == "error"));
866 }
867
868 #[test]
869 fn htsget_error_parse_error() {
870 let result = HtsGetError::parse_error("error");
871 assert!(matches!(result, HtsGetError::ParseError(message) if message == "error"));
872 }
873
874 #[test]
875 fn htsget_error_internal_error() {
876 let result = HtsGetError::internal_error("error");
877 assert!(matches!(result, HtsGetError::InternalError(message) if message == "error"));
878 }
879
880 #[test]
881 fn query_new() {
882 let result = Query::new_with_default_request("NA12878", Format::Bam);
883 assert_eq!(result.id(), "NA12878");
884 }
885
886 #[test]
887 fn query_with_format() {
888 let result = Query::new_with_default_request("NA12878", Format::Bam);
889 assert_eq!(result.format(), Format::Bam);
890 }
891
892 #[test]
893 fn query_with_class() {
894 let result = Query::new_with_default_request("NA12878", Format::Bam).with_class(Class::Header);
895 assert_eq!(result.class(), Class::Header);
896 }
897
898 #[test]
899 fn query_with_reference_name() {
900 let result =
901 Query::new_with_default_request("NA12878", Format::Bam).with_reference_name("chr1");
902 assert_eq!(result.reference_name(), Some("chr1"));
903 }
904
905 #[test]
906 fn query_with_start() {
907 let result = Query::new_with_default_request("NA12878", Format::Bam).with_start(0);
908 assert_eq!(result.interval().start(), Some(0));
909 }
910
911 #[test]
912 fn query_with_end() {
913 let result = Query::new_with_default_request("NA12878", Format::Bam).with_end(0);
914 assert_eq!(result.interval().end(), Some(0));
915 }
916
917 #[test]
918 fn query_with_fields() {
919 let result = Query::new_with_default_request("NA12878", Format::Bam).with_fields(Fields::List(
920 HashSet::from_iter(vec!["QNAME".to_string(), "FLAG".to_string()]),
921 ));
922 assert_eq!(
923 result.fields(),
924 &Fields::List(HashSet::from_iter(vec![
925 "QNAME".to_string(),
926 "FLAG".to_string()
927 ]))
928 );
929 }
930
931 #[test]
932 fn query_with_tags() {
933 let result = Query::new_with_default_request("NA12878", Format::Bam)
934 .with_tags(Tags::Tagged(TaggedTypeAll::All));
935 assert_eq!(result.tags(), &Tags::Tagged(TaggedTypeAll::All));
936 }
937
938 #[test]
939 fn query_with_no_tags() {
940 let result =
941 Query::new_with_default_request("NA12878", Format::Bam).with_no_tags(vec!["RG", "OQ"]);
942 assert_eq!(
943 result.no_tags(),
944 &NoTags(Some(HashSet::from_iter(vec![
945 "RG".to_string(),
946 "OQ".to_string()
947 ])))
948 );
949 }
950
951 #[test]
952 fn format_from_bam() {
953 let result = String::from(Format::Bam);
954 assert_eq!(result, "BAM");
955 }
956
957 #[test]
958 fn format_from_cram() {
959 let result = String::from(Format::Cram);
960 assert_eq!(result, "CRAM");
961 }
962
963 #[test]
964 fn format_from_vcf() {
965 let result = String::from(Format::Vcf);
966 assert_eq!(result, "VCF");
967 }
968
969 #[test]
970 fn format_from_bcf() {
971 let result = String::from(Format::Bcf);
972 assert_eq!(result, "BCF");
973 }
974
975 #[test]
976 fn headers_with_header() {
977 let header = Headers::new(HashMap::new()).with_header("Range", "bytes=0-1023");
978 let result = header.0.get("Range");
979 assert_eq!(result, Some(&"bytes=0-1023".to_string()));
980 }
981
982 #[test]
983 fn headers_is_empty() {
984 assert!(Headers::new(HashMap::new()).is_empty());
985 }
986
987 #[test]
988 fn headers_insert() {
989 let mut header = Headers::new(HashMap::new());
990 header.insert("Range", "bytes=0-1023");
991 let result = header.0.get("Range");
992 assert_eq!(result, Some(&"bytes=0-1023".to_string()));
993 }
994
995 #[test]
996 fn headers_extend() {
997 let mut headers = Headers::new(HashMap::new());
998 headers.insert("Range", "bytes=0-1023");
999
1000 let mut extend_with = Headers::new(HashMap::new());
1001 extend_with.insert("header", "value");
1002
1003 headers.extend(extend_with);
1004
1005 let result = headers.0.get("Range");
1006 assert_eq!(result, Some(&"bytes=0-1023".to_string()));
1007
1008 let result = headers.0.get("header");
1009 assert_eq!(result, Some(&"value".to_string()));
1010 }
1011
1012 #[test]
1013 fn headers_multiple_values() {
1014 let headers = Headers::new(HashMap::new())
1015 .with_header("Range", "bytes=0-1023")
1016 .with_header("Range", "bytes=1024-2047");
1017 let result = headers.0.get("Range");
1018
1019 assert_eq!(result, Some(&"bytes=0-1023, bytes=1024-2047".to_string()));
1020 }
1021
1022 #[test]
1023 fn headers_try_from_header_map() {
1024 let mut headers = HeaderMap::new();
1025 headers.append(
1026 HeaderName::from_str("Range").unwrap(),
1027 HeaderValue::from_str("bytes=0-1023").unwrap(),
1028 );
1029 headers.append(
1030 HeaderName::from_str("Range").unwrap(),
1031 HeaderValue::from_str("bytes=1024-2047").unwrap(),
1032 );
1033 headers.append(
1034 HeaderName::from_str("Range").unwrap(),
1035 HeaderValue::from_str("bytes=2048-3071, bytes=3072-4095").unwrap(),
1036 );
1037 let headers: Headers = (&headers).try_into().unwrap();
1038
1039 let result = headers.0.get("range");
1040 assert_eq!(
1041 result,
1042 Some(&"bytes=0-1023, bytes=1024-2047, bytes=2048-3071, bytes=3072-4095".to_string())
1043 );
1044 }
1045
1046 #[test]
1047 fn serialize_headers() {
1048 let headers = Headers::new(HashMap::new())
1049 .with_header("Range", "bytes=0-1023")
1050 .with_header("Range", "bytes=1024-2047");
1051
1052 let result = to_value(headers).unwrap();
1053 assert_eq!(
1054 result,
1055 json!({
1056 "Range" : "bytes=0-1023, bytes=1024-2047"
1057 })
1058 );
1059 }
1060
1061 #[test]
1062 fn url_with_headers() {
1063 let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")
1064 .with_headers(Headers::new(HashMap::new()));
1065 assert_eq!(result.headers, None);
1066 }
1067
1068 #[test]
1069 fn url_add_headers() {
1070 let mut headers = Headers::new(HashMap::new());
1071 headers.insert("Range", "bytes=0-1023");
1072
1073 let mut extend_with = Headers::new(HashMap::new());
1074 extend_with.insert("header", "value");
1075
1076 let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")
1077 .with_headers(headers)
1078 .add_headers(extend_with);
1079
1080 let expected_headers = Headers::new(HashMap::new())
1081 .with_header("Range", "bytes=0-1023")
1082 .with_header("header", "value");
1083
1084 assert_eq!(result.headers, Some(expected_headers));
1085 }
1086
1087 #[test]
1088 fn url_with_class() {
1089 let result =
1090 Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==").with_class(Class::Header);
1091 assert_eq!(result.class, Some(Class::Header));
1092 }
1093
1094 #[test]
1095 fn url_set_class() {
1096 let result =
1097 Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==").set_class(Some(Class::Header));
1098 assert_eq!(result.class, Some(Class::Header));
1099 }
1100
1101 #[test]
1102 fn url_new() {
1103 let result = Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==");
1104 assert_eq!(result.url, "data:application/vnd.ga4gh.bam;base64,QkFNAQ==");
1105 assert_eq!(result.headers, None);
1106 assert_eq!(result.class, None);
1107 }
1108
1109 #[test]
1110 fn response_new() {
1111 let result = Response::new(
1112 Format::Bam,
1113 vec![Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")],
1114 );
1115 assert_eq!(result.format, Format::Bam);
1116 assert_eq!(
1117 result.urls,
1118 vec![Url::new("data:application/vnd.ga4gh.bam;base64,QkFNAQ==")]
1119 );
1120 }
1121}