bio_types/annot/loc.rs
1// Copyright 2017 Nicholas Ingolia
2// Licensed under the MIT license (http://opensource.org/licenses/MIT)
3// This file may not be copied, modified, or distributed
4// except according to those terms.
5
6//! Trait shared across sequence locations -- spliced, contiguous, or
7//! single-position.
8
9use std::ops::Neg;
10
11use crate::annot::contig::Contig;
12use crate::annot::pos::Pos;
13
14use crate::strand::*;
15
16/// A trait for a sequence location -- a defined region on a named
17/// chromosome (or other reference sequence), which may also have
18/// defined strand information. The trait is generic over the type of
19/// identifier for the reference sequence (allowing owned strings,
20/// sequence IDs, and other options) and the strand information
21/// (allowing type-level distinction between stranded and unstranded
22/// annotations).
23pub trait Loc {
24 type RefID;
25 type Strand;
26
27 /// Name of the reference sequence (chromosome name, etc.)
28 fn refid(&self) -> &Self::RefID;
29 /// Starting (lowest, left-most, 5'-most) position on the
30 /// reference sequence (0-based).
31 fn start(&self) -> isize;
32 /// Length of the region
33 fn length(&self) -> usize;
34 /// `Strand` of the position
35 fn strand(&self) -> Self::Strand
36 where
37 Self::Strand: Copy;
38
39 /// Map a sequence position on a reference sequence _into_ a
40 /// relative position within an annotated location on the
41 /// reference sequence.
42 ///
43 /// The first position of the annotated location is mapped to a
44 /// position at 0, the next position of the annotated location is
45 /// mapped to a position at 1, and so forth. The annotated
46 /// location must have a known strandedness, which is taken into
47 /// account. For reverse-strand annotations, the 3'-most position
48 /// on the reference sequence is mapped to 0, and the strandedness
49 /// of the position is reversed. When the sequence position lies
50 /// on a different named reference sequence than the annotated
51 /// location, or doesn't fall within the annotated location, then
52 /// `None` is returned.
53 ///
54 /// This function serves as an inverse of @pos_outof.
55 fn pos_into<T>(&self, pos: &Pos<Self::RefID, T>) -> Option<Pos<(), T>>
56 where
57 Self::RefID: Eq,
58 Self::Strand: Into<ReqStrand> + Copy,
59 T: Neg<Output = T> + Copy;
60
61 /// Map a relative position within an annotated location _out of_
62 /// that location onto the enclosing reference sequence.
63 ///
64 /// Position 0 within the annotated location is mapped to the
65 /// first position of the annotated location, position 1 is mapped
66 /// to the subsequent position, and so forth. The annotated
67 /// location must have a known strandedness, which is taken into
68 /// account. For reverse-strand annotations, position 0 is mapped
69 /// to the 3'-most position of the reference sequence. When the
70 /// sequence position is either negative, or greater than the
71 /// length of the annotated location, then `None` is returned. The
72 /// reference name for the sequence position is discarded; the
73 /// mapped position receives a clone of the annotation's reference
74 /// sequence name.
75 ///
76 /// This function serves as an inverse of @pos_into.
77 fn pos_outof<Q, T>(&self, pos: &Pos<Q, T>) -> Option<Pos<Self::RefID, T>>
78 where
79 Self::RefID: Clone,
80 Self::Strand: Into<ReqStrand> + Copy,
81 T: Neg<Output = T> + Copy;
82
83 fn contig_intersection<T>(&self, other: &Contig<Self::RefID, T>) -> Option<Self>
84 where
85 Self: ::std::marker::Sized,
86 Self::RefID: PartialEq + Clone,
87 Self::Strand: Copy;
88
89 /// Contiguous sequence location that fully covers the location.
90 fn contig(&self) -> Contig<Self::RefID, Self::Strand>
91 where
92 Self::RefID: Clone,
93 Self::Strand: Copy,
94 {
95 Contig::new(
96 self.refid().clone(),
97 self.start(),
98 self.length(),
99 self.strand(),
100 )
101 }
102
103 /// The first `Pos` in a location, on the annotated strand.
104 ///
105 /// The first position in a zero-length annotation will be the
106 /// starting postion. This is the same as the first position in a
107 /// length-1 annotation, on either strand.
108 fn first_pos(&self) -> Pos<Self::RefID, Self::Strand>
109 where
110 Self::RefID: Clone,
111 Self::Strand: Into<ReqStrand> + Copy,
112 {
113 match self.strand().into() {
114 ReqStrand::Forward => Pos::new(self.refid().clone(), self.start(), self.strand()),
115 ReqStrand::Reverse => {
116 if self.length() == 0 {
117 Pos::new(self.refid().clone(), self.start(), self.strand())
118 } else {
119 Pos::new(
120 self.refid().clone(),
121 self.start() + (self.length() as isize) - 1,
122 self.strand(),
123 )
124 }
125 }
126 }
127 }
128
129 /// The last `Pos` in a location, on the annotated strand.
130 ///
131 /// The last position in a zero-length annotation will be the
132 /// starting postion. This is the same as the last position in a
133 /// length-1 annotation, on either strand.
134 fn last_pos(&self) -> Pos<Self::RefID, Self::Strand>
135 where
136 Self::RefID: Clone,
137 Self::Strand: Into<ReqStrand> + Copy,
138 {
139 match self.strand().into() {
140 ReqStrand::Forward => {
141 if self.length() == 0 {
142 Pos::new(self.refid().clone(), self.start(), self.strand())
143 } else {
144 Pos::new(
145 self.refid().clone(),
146 self.start() + (self.length() as isize) - 1,
147 self.strand(),
148 )
149 }
150 }
151 ReqStrand::Reverse => Pos::new(self.refid().clone(), self.start(), self.strand()),
152 }
153 }
154}