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}