Support empty ranges

This commit is contained in:
Steven Fackler 2013-10-31 00:04:45 -07:00
parent f54d40aff8
commit edc4927eb6
3 changed files with 99 additions and 89 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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]