Support empty ranges
This commit is contained in:
parent
f54d40aff8
commit
edc4927eb6
3
tests.rs
3
tests.rs
@ -368,7 +368,8 @@ macro_rules! test_range(
|
||||
(Some(Range::new(Some(RangeBound::new($low, Exclusive)),
|
||||
Some(RangeBound::new($high, Exclusive)))),
|
||||
"'(" + $low_str + "," + $high_str + ")'"),
|
||||
(None, ~"NULL")];
|
||||
(Some(Range::empty()), ~"'empty'"),
|
||||
(None, ~"NULL")];
|
||||
let test_refs: ~[(Option<Range<$t>>, &str)] = tests.iter()
|
||||
.map(|&(ref val, ref s)| { (val.clone(), s.as_slice()) })
|
||||
.collect();
|
||||
|
79
types/mod.rs
79
types/mod.rs
@ -48,6 +48,7 @@ static RANGE_UPPER_UNBOUNDED: i8 = 0b0001_0000;
|
||||
static RANGE_LOWER_UNBOUNDED: i8 = 0b0000_1000;
|
||||
static RANGE_UPPER_INCLUSIVE: i8 = 0b0000_0100;
|
||||
static RANGE_LOWER_INCLUSIVE: i8 = 0b0000_0010;
|
||||
static RANGE_EMPTY: i8 = 0b0000_0001;
|
||||
|
||||
/// A Postgres type
|
||||
#[deriving(Eq)]
|
||||
@ -281,30 +282,36 @@ macro_rules! from_range_impl(
|
||||
let mut rdr = BufReader::new(buf.as_slice());
|
||||
let t = rdr.read_i8();
|
||||
|
||||
let lower = match t & RANGE_LOWER_UNBOUNDED {
|
||||
0 => {
|
||||
let type_ = match t & RANGE_LOWER_INCLUSIVE {
|
||||
0 => Exclusive,
|
||||
_ => Inclusive
|
||||
};
|
||||
rdr.read_be_i32();
|
||||
Some(RangeBound::new(RawFromSql::raw_from_sql(&mut rdr), type_))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
let upper = match t & RANGE_UPPER_UNBOUNDED {
|
||||
0 => {
|
||||
let type_ = match t & RANGE_UPPER_INCLUSIVE {
|
||||
0 => Exclusive,
|
||||
_ => Inclusive
|
||||
};
|
||||
rdr.read_be_i32();
|
||||
Some(RangeBound::new(RawFromSql::raw_from_sql(&mut rdr), type_))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
if t & RANGE_EMPTY != 0 {
|
||||
Range::empty()
|
||||
} else {
|
||||
let lower = match t & RANGE_LOWER_UNBOUNDED {
|
||||
0 => {
|
||||
let type_ = match t & RANGE_LOWER_INCLUSIVE {
|
||||
0 => Exclusive,
|
||||
_ => Inclusive
|
||||
};
|
||||
rdr.read_be_i32();
|
||||
Some(RangeBound::new(RawFromSql::raw_from_sql(&mut rdr),
|
||||
type_))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
let upper = match t & RANGE_UPPER_UNBOUNDED {
|
||||
0 => {
|
||||
let type_ = match t & RANGE_UPPER_INCLUSIVE {
|
||||
0 => Exclusive,
|
||||
_ => Inclusive
|
||||
};
|
||||
rdr.read_be_i32();
|
||||
Some(RangeBound::new(RawFromSql::raw_from_sql(&mut rdr),
|
||||
type_))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
Range::new(lower, upper)
|
||||
Range::new(lower, upper)
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@ -495,17 +502,21 @@ macro_rules! to_range_impl(
|
||||
let mut buf = MemWriter::new();
|
||||
|
||||
let mut tag = 0;
|
||||
match self.lower() {
|
||||
&None => tag |= RANGE_LOWER_UNBOUNDED,
|
||||
&Some(RangeBound { type_: Inclusive, _ }) =>
|
||||
tag |= RANGE_LOWER_INCLUSIVE,
|
||||
_ => {}
|
||||
}
|
||||
match self.upper() {
|
||||
&None => tag |= RANGE_UPPER_UNBOUNDED,
|
||||
&Some(RangeBound { type_: Inclusive, _ }) =>
|
||||
tag |= RANGE_UPPER_INCLUSIVE,
|
||||
_ => {}
|
||||
if self.is_empty() {
|
||||
tag |= RANGE_EMPTY;
|
||||
} else {
|
||||
match self.lower() {
|
||||
&None => tag |= RANGE_LOWER_UNBOUNDED,
|
||||
&Some(RangeBound { type_: Inclusive, _ }) =>
|
||||
tag |= RANGE_LOWER_INCLUSIVE,
|
||||
_ => {}
|
||||
}
|
||||
match self.upper() {
|
||||
&None => tag |= RANGE_UPPER_UNBOUNDED,
|
||||
&Some(RangeBound { type_: Inclusive, _ }) =>
|
||||
tag |= RANGE_UPPER_INCLUSIVE,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
buf.write_i8(tag);
|
||||
|
106
types/range.rs
106
types/range.rs
@ -127,22 +127,12 @@ impl<S: BoundSided, T: Ord> RangeBound<S, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The relation of a value to a range
|
||||
#[deriving(Eq)]
|
||||
pub enum RangeComparison {
|
||||
/// The value lies above the range
|
||||
Above,
|
||||
/// The value lies within the range
|
||||
Within,
|
||||
/// The value lies below the range
|
||||
Below
|
||||
}
|
||||
|
||||
/// Represents a range of values.
|
||||
#[deriving(Eq,Clone)]
|
||||
pub struct Range<T> {
|
||||
priv lower: Option<RangeBound<LowerBound, T>>,
|
||||
priv upper: Option<RangeBound<UpperBound, T>>,
|
||||
pub enum Range<T> {
|
||||
priv Empty,
|
||||
priv Normal(Option<RangeBound<LowerBound, T>>,
|
||||
Option<RangeBound<UpperBound, T>>)
|
||||
}
|
||||
|
||||
impl<T: Ord+Normalizable> Range<T> {
|
||||
@ -160,39 +150,47 @@ impl<T: Ord+Normalizable> Range<T> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Range { lower: lower, upper: upper }
|
||||
Normal(lower, upper)
|
||||
}
|
||||
|
||||
/// Creates a new empty range.
|
||||
pub fn empty() -> Range<T> {
|
||||
Empty
|
||||
}
|
||||
|
||||
/// Determines if this range is the empty range.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match *self {
|
||||
Empty => true,
|
||||
Normal(*) => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the lower bound if it exists.
|
||||
pub fn lower<'a>(&'a self) -> &'a Option<RangeBound<LowerBound, T>> {
|
||||
&self.lower
|
||||
match *self {
|
||||
Empty => &None,
|
||||
Normal(ref lower, _) => lower
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the upper bound if it exists.
|
||||
pub fn upper<'a>(&'a self) -> &'a Option<RangeBound<UpperBound, T>> {
|
||||
&self.upper
|
||||
}
|
||||
|
||||
/// Compares a value to this range.
|
||||
pub fn cmp(&self, value: &T) -> RangeComparison {
|
||||
let lower = do self.lower.as_ref().map_default(true) |b| {
|
||||
b.in_bounds(value)
|
||||
};
|
||||
let upper = do self.upper.as_ref().map_default(true) |b| {
|
||||
b.in_bounds(value)
|
||||
};
|
||||
|
||||
match (lower, upper) {
|
||||
(true, false) => Above,
|
||||
(true, true) => Within,
|
||||
(false, true) => Below,
|
||||
_ => unreachable!()
|
||||
match *self {
|
||||
Empty => &None,
|
||||
Normal(_, ref upper) => upper
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a value lies within this range.
|
||||
pub fn contains(&self, value: &T) -> bool {
|
||||
self.cmp(value) == Within
|
||||
match *self {
|
||||
Empty => false,
|
||||
Normal(ref lower, ref upper) => {
|
||||
lower.as_ref().map_default(true, |b| { b.in_bounds(value) }) &&
|
||||
upper.as_ref().map_default(true, |b| { b.in_bounds(value) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,37 +261,37 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_cmp() {
|
||||
fn test_range_contains() {
|
||||
let r = Range::new(Some(RangeBound::new(1i32, Inclusive)),
|
||||
Some(RangeBound::new(3i32, Inclusive)));
|
||||
assert_eq!(Above, r.cmp(&4));
|
||||
assert_eq!(Within, r.cmp(&3));
|
||||
assert_eq!(Within, r.cmp(&2));
|
||||
assert_eq!(Within, r.cmp(&1));
|
||||
assert_eq!(Below, r.cmp(&0));
|
||||
assert!(!r.contains(&4));
|
||||
assert!(r.contains(&3));
|
||||
assert!(r.contains(&2));
|
||||
assert!(r.contains(&1));
|
||||
assert!(!r.contains(&0));
|
||||
|
||||
let r = Range::new(Some(RangeBound::new(1i32, Exclusive)),
|
||||
Some(RangeBound::new(3i32, Exclusive)));
|
||||
assert_eq!(Above, r.cmp(&4));
|
||||
assert_eq!(Above, r.cmp(&3));
|
||||
assert_eq!(Within, r.cmp(&2));
|
||||
assert_eq!(Below, r.cmp(&1));
|
||||
assert_eq!(Below, r.cmp(&0));
|
||||
assert!(!r.contains(&4));
|
||||
assert!(!r.contains(&3));
|
||||
assert!(r.contains(&2));
|
||||
assert!(!r.contains(&1));
|
||||
assert!(!r.contains(&0));
|
||||
|
||||
let r = Range::new(None, Some(RangeBound::new(3i32, Inclusive)));
|
||||
assert_eq!(Above, r.cmp(&4));
|
||||
assert_eq!(Within, r.cmp(&2));
|
||||
assert_eq!(Within, r.cmp(&Bounded::min_value()));
|
||||
assert!(!r.contains(&4));
|
||||
assert!(r.contains(&2));
|
||||
assert!(r.contains(&Bounded::min_value()));
|
||||
|
||||
let r = Range::new(Some(RangeBound::new(1i32, Inclusive)), None);
|
||||
assert_eq!(Within, r.cmp(&Bounded::max_value()));
|
||||
assert_eq!(Within, r.cmp(&4));
|
||||
assert_eq!(Below, r.cmp(&0));
|
||||
assert!(r.contains(&Bounded::max_value()));
|
||||
assert!(r.contains(&4));
|
||||
assert!(!r.contains(&0));
|
||||
|
||||
let r = Range::new(None, None);
|
||||
assert_eq!(Within, r.cmp(&Bounded::max_value()));
|
||||
assert_eq!(Within, r.cmp(&0i32));
|
||||
assert_eq!(Within, r.cmp(&Bounded::min_value()));
|
||||
assert!(r.contains(&Bounded::max_value()));
|
||||
assert!(r.contains(&0i32));
|
||||
assert!(r.contains(&Bounded::min_value()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user