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
//! Defines the `AgeRange` field for the `entries` table.
use std::fmt;
use crate::Age;
/// The AgeRange used by the checker for interpreting age data.
///
/// The ages in an AgeRange are always `Age::Exact` or `Age::None`.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct AgeRange {
pub min: Age,
pub max: Age,
}
impl Default for AgeRange {
fn default() -> AgeRange {
AgeRange {
min: Age::None,
max: Age::None,
}
}
}
impl From<Age> for AgeRange {
/// Create an AgeRange based on a single Age.
fn from(age: Age) -> AgeRange {
let (min, max) = match age {
Age::Exact(n) => (n, n),
Age::Approximate(n) => (n, n + 1),
Age::None => return AgeRange::default(),
};
AgeRange {
min: Age::Exact(min),
max: Age::Exact(max),
}
}
}
impl From<(Age, Age)> for AgeRange {
/// Create an AgeRange based on a range of Ages.
fn from(range: (Age, Age)) -> AgeRange {
let min_age = match range.0 {
Age::Exact(0) => Age::None,
Age::Exact(n) => Age::Exact(n),
Age::Approximate(n) => Age::Exact(n),
Age::None => Age::None,
};
let max_age = match range.1 {
Age::Exact(255) => Age::None,
Age::Exact(n) => Age::Exact(n),
Age::Approximate(n) => Age::Exact(n + 1),
Age::None => Age::None,
};
// TODO: Ensure that min <= max if both are Exact.
AgeRange {
min: min_age,
max: max_age,
}
}
}
impl AgeRange {
/// Whether the AgeRange is the default, unassigned AgeRange.
pub fn is_none(self) -> bool {
self == AgeRange::default()
}
/// The opposite of AgeRange::is_none().
pub fn is_some(self) -> bool {
self != AgeRange::default()
}
/// Intersects this AgeRange with another.
pub fn intersect(self, other: AgeRange) -> AgeRange {
let mut acc = self;
if other.min.is_some() && (acc.min.is_none() || other.min > acc.min) {
acc.min = other.min;
}
if other.max.is_some() && (acc.max.is_none() || other.max < acc.max) {
acc.max = other.max;
}
if acc.min.is_some() && acc.max.is_some() && acc.min.is_definitely_greater_than(acc.max) {
AgeRange::default()
} else {
acc
}
}
/// Calculates the number of years between the endpoints, if well-defined.
///
/// # Examples
///
/// ```
/// # use opltypes::{Age, AgeRange};
/// let range = AgeRange::from((Age::Exact(40), Age::Exact(44)));
/// assert_eq!(range.distance(), Some(4));
///
/// let range = AgeRange::from((Age::Exact(30), Age::Exact(30)));
/// assert_eq!(range.distance(), Some(0));
///
/// let range = AgeRange::from(Age::Approximate(30));
/// assert_eq!(range.distance(), Some(1));
///
/// let range = AgeRange::from((Age::None, Age::Exact(55)));
/// assert_eq!(range.distance(), None);
/// ```
pub fn distance(self) -> Option<u8> {
if let Age::Exact(min) = self.min {
if let Age::Exact(max) = self.max {
return Some(max - min);
}
}
None
}
}
impl fmt::Display for AgeRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match (self.min.is_some(), self.max.is_some()) {
(false, false) => Ok(()),
(false, true) => write!(f, "0-{}", self.max),
(true, false) => write!(f, "{}-999", self.min),
(true, true) => write!(f, "{}-{}", self.min, self.max),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_age() {
let approx_40 = Age::Approximate(40);
assert_eq!(
AgeRange::from(approx_40),
AgeRange::from((Age::Exact(40), Age::Exact(41)))
);
}
}