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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! This is a little library that helps with range and bounds checking. It works
//! with Rust’s standard `Range`, `RangeFrom`, and `RangeTo` types.
//!
//!
//! ## Checking whether a range contains a value
//!
//! The trait `Contains` is implemented on the range types. As long as the
//! data type in question is `PartialOrd`, it can be used to check whether a
//! value of that type is contained within a range:
//!
//! ```
//! use range_check::Contains;
//!
//! let range = 3000..5000;
//! assert!(range.contains(4123));
//!
//! let range = 10..;
//! assert!(range.contains(23));
//! ```
//!
//! There’s also the `Within` trait, which does the same check, only with the
//! range as the argument:
//!
//! ```
//! use range_check::Within;
//!
//! assert!(4123.is_within(3000..5000));
//! assert!(23.is_within(10..));
//! ```
//!
//!
//! ## Failing early if a value is outside a range
//!
//! It can sometimes be more helpful to automatically return a failure case,
//! such as with the `try!` macro, than just check whether a value is inside a
//! range. The `Check` trait returns `Result`s that contain debugging
//! information for when a value doesn’t lie within a range:
//!
//! ```
//! use range_check::Check;
//!
//! struct Clock {
//!     hour: i8,
//!     minute: i8,
//! }
//!
//! impl Clock {
//!     fn new(hour: i8, minute: i8) -> range_check::Result<Clock, i8> {
//!         Ok(Clock {
//!             hour:   try!(hour.check_range(0..24)),
//!             minute: try!(minute.check_range(0..60)),
//!         })
//!     }
//! }
//!
//! assert!(Clock::new(23, 59).is_ok());
//! assert!(Clock::new(24, 00).is_err());
//! ```
//!
//! Displaying the `Error` that gets returned in the error case shows you
//! exactly which range failed to be satisfied:
//!
//! ```
//! use std::string::ToString;
//! use range_check::Check;
//!
//! let failure = 13.check_range(0..10).unwrap_err();
//! assert_eq!(failure.to_string(), "value (13) outside of range (0 .. 10)");
//! ```


#![crate_name = "range_check"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]

#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_qualifications)]
#![warn(unused_results)]

use std::borrow::Borrow;
use std::ops::{Range, RangeFrom, RangeTo};

mod bounds;
pub use bounds::{Bounds, Bounded};

mod result;
pub use result::{Check, Result, Error};


/// A trait for all range-type values.
pub trait Contains<T> {

    /// Returns `true` if this range contains the given value, `false`
    /// otherwise.
    ///
    /// Supports both values and references of the type to check.
    ///
    /// ### Examples
    ///
    /// ```
    /// use range_check::Contains;
    ///
    /// let range = 3000..5000;
    /// assert!(range.contains(4123));
    /// ```
    fn contains<TRef: Borrow<T>>(&self, value: TRef) -> bool;
}

impl<T> Contains<T> for Range<T>
where T: PartialOrd {
    fn contains<TRef: Borrow<T>>(&self, value: TRef) -> bool {
        (value.borrow() >= &self.start) && (value.borrow() < &self.end)
    }
}

impl<T> Contains<T> for RangeFrom<T>
where T: PartialOrd {
    fn contains<TRef: Borrow<T>>(&self, value: TRef) -> bool {
        value.borrow() >= &self.start
    }
}

impl<T> Contains<T> for RangeTo<T>
where T: PartialOrd {
    fn contains<TRef: Borrow<T>>(&self, value: TRef) -> bool {
        value.borrow() < &self.end
    }
}


/// A trait for values that could be contained in a range.
pub trait Within<R, RRef: Borrow<R>>: Sized {

    /// Returns `true` if this value is contained within the given range;
    /// `false` otherwise.
    ///
    /// Supports both values and references as the range argument.
    ///
    /// ### Examples
    ///
    /// ```
    /// use range_check::Within;
    ///
    /// assert!(4123.is_within(3000..5000));
    /// ```
    fn is_within(&self, range: RRef) -> bool;
}

impl<T, R> Within<R, R> for T
where R: Contains<T> {
    fn is_within(&self, range: R) -> bool {
        range.borrow().contains(self)
    }
}

impl<'a, T, R> Within<R, &'a R> for T
where R: Contains<T> {
    fn is_within(&self, range: &'a R) -> bool {
        range.borrow().contains(self)
    }
}


#[cfg(test)]
mod test {
    use super::{Contains, Within};

    #[test]
    fn yes() {
        assert!((1..5).contains(3));
        assert!(3.is_within(1..5));
    }

    #[test]
    fn no() {
        assert!(!(1..5).contains(&7));
        assert!(!(7.is_within(1..5)));
    }

    #[test]
    fn from_yes() {
        assert!((1..).contains(&3));
        assert!(3.is_within(1..));
    }

    #[test]
    fn from_no() {
        assert!(!(1..).contains(&-7));
        assert!(!(-7).is_within(1..));
    }

    #[test]
    fn to_yes() {
        assert!((..5).contains(&3));
        assert!(3.is_within(..5));
    }

    #[test]
    fn to_no() {
        assert!(!(..5).contains(&7));
        assert!(!7.is_within(&(..5)));
    }
}