omics_coordinate/position/
zero.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! 0-based, half-open [`Position`](crate::Position)s.

use crate::position;
use crate::position::Error;
use crate::position::ParseError;
use crate::position::Result;
use crate::position::Value;
use crate::system::Zero;

mod addition;
mod subtraction;

/// A 0-based, half-open [`Position`](crate::Position).
pub type Position = crate::Position<Zero>;

impl position::r#trait::Position<Zero> for Position {
    /// Creates a new [`Position`].
    ///
    /// Examples
    ///
    /// ```
    /// use omics_coordinate::position::Value;
    /// use omics_coordinate::position::zero::Position;
    ///
    /// // Usize
    ///
    /// let value = Value::Usize(0);
    /// let position = Position::try_new(value)?;
    ///
    /// // Lower bound
    ///
    /// let value = Value::LowerBound;
    /// let position = Position::try_new(value)?;
    ///
    /// # Ok::<(), Box<dyn std::error::Error>>(())
    /// ```
    fn try_new(value: impl Into<Value>) -> Result<Self> {
        Ok(Self {
            system: Zero,
            inner: value.into(),
        })
    }
}

impl Position {
    /// Creates a [`Position`] with a [`Value::LowerBound`].
    ///
    /// # Examples
    ///
    /// ```
    /// use omics_coordinate::Position;
    /// use omics_coordinate::position::Value;
    /// use omics_coordinate::system::Zero;
    ///
    /// // 1-based position
    ///
    /// let position = Position::<Zero>::lower_bound();
    /// assert_eq!(position.inner(), &Value::LowerBound);
    ///
    /// // 0-based position
    ///
    /// let position = Position::<Zero>::lower_bound();
    /// assert_eq!(position.inner(), &Value::LowerBound);
    /// ```
    pub fn lower_bound() -> Self {
        Self {
            system: Zero,
            inner: Value::LowerBound,
        }
    }
}

impl std::str::FromStr for Position {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        let value = s
            .parse::<Value>()
            .map_err(|err| Error::Parse(ParseError::ValueError(err)))?;

        Self::try_new(value)
    }
}

impl From<usize> for Position {
    fn from(value: usize) -> Self {
        // SAFETY: a zero-based position accepts any usize, so this will never
        // fail and can be safely unwrapped.
        Self::try_new(Value::Usize(value)).unwrap()
    }
}

impl From<Value> for Position {
    fn from(value: Value) -> Self {
        // SAFETY: a zero-based position accepts any [`Value`], so this will
        // never fail and can be safely unwrapped.
        Self::try_new(value).unwrap()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_zero_based_position_creation() {
        let position = Position::from(10);
        assert_eq!(position.inner(), &Value::Usize(10));
    }

    #[test]
    fn test_negative_position_creation() {
        let position = Position::lower_bound();
        assert_eq!(position.inner(), &Value::LowerBound);
    }

    #[test]
    fn test_ordering_of_positions() {
        // Ordering of two zero-based positions
        let a = Position::from(10);
        let b = Position::from(5);
        assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Greater));
        assert_eq!(a.cmp(&b), std::cmp::Ordering::Greater);

        // Ordering of a zero-based position and a lower bound
        let a = Position::from(0);
        let b = Position::lower_bound();
        assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Greater));
        assert_eq!(a.cmp(&b), std::cmp::Ordering::Greater);

        // Ordering of a lower bound and a zero-based position
        let a = Position::lower_bound();
        let b = Position::from(0);
        assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Less));
        assert_eq!(a.cmp(&b), std::cmp::Ordering::Less);

        // Ordering of two lower bounds
        let a = Position::lower_bound();
        let b = Position::lower_bound();
        assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Equal));
        assert_eq!(a.cmp(&b), std::cmp::Ordering::Equal);
    }

    #[test]
    fn test_usize_into_position() {
        let position: Position = 30usize.into();
        assert_eq!(position.inner(), &Value::Usize(30));
    }

    #[test]
    fn it_remains_the_size_of_the_inner_value() {
        assert_eq!(
            std::mem::size_of::<Position>(),
            std::mem::size_of::<Value>()
        )
    }
}