willow_data_model/groupings/willow_range.rs
1use core::cmp::Ordering;
2use core::fmt::{self, Debug, Formatter};
3use core::hash::Hash;
4use core::ops::{Bound, RangeBounds};
5
6#[cfg(feature = "dev")]
7use arbitrary::{Arbitrary, size_hint};
8
9use order_theory::{
10 GreatestElement, LeastElement, SuccessorExceptForGreatest,
11};
12
13use crate::prelude::EmptyGrouping;
14
15/// A non-empty [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) with an inclusive start value and a strictly-greater exclusive end value.
16///
17/// It includes all values that are greater than or equal to the start value *and* strictly less than the end value.
18///
19/// ```
20/// use willow_data_model::prelude::*;
21///
22/// let r = ClosedRange::new(2, 7);
23/// assert_eq!(r.start(), &2);
24/// assert_eq!(r.end(), &7);
25///
26/// assert!(ClosedRange::try_new(7, 2).is_err());
27/// assert!(ClosedRange::try_new(2, 2).is_err());
28/// ```
29#[derive(Clone, Copy, PartialEq, Eq, Hash)]
30#[repr(C)]
31pub struct ClosedRange<T> {
32 start: T,
33 end: T,
34}
35
36impl<T: Debug> Debug for ClosedRange<T> {
37 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
38 self.start.fmt(f)?;
39 write!(f, "..")?;
40 self.end.fmt(f)?;
41 Ok(())
42 }
43}
44
45impl<T> PartialOrd for ClosedRange<T>
46where
47 T: PartialOrd,
48{
49 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
50 match (
51 self.start.partial_cmp(&other.start)?,
52 self.end.partial_cmp(&other.end)?,
53 ) {
54 (Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
55 (cmp_start, cmp_end) => {
56 if cmp_start.is_le() && cmp_end.is_ge() {
57 Some(Ordering::Greater)
58 } else if cmp_start.is_ge() && cmp_end.is_le() {
59 Some(Ordering::Less)
60 } else {
61 None
62 }
63 }
64 }
65 }
66}
67
68impl<T> ClosedRange<T>
69where
70 T: PartialOrd,
71{
72 /// Creates a new closed range, or an [`EmptyGrouping`] error if `start >= end`.
73 ///
74 /// ```
75 /// use willow_data_model::prelude::*;
76 ///
77 /// assert!(ClosedRange::try_new(2, 7).is_ok());
78 /// assert!(ClosedRange::try_new(7, 2).is_err());
79 /// assert!(ClosedRange::try_new(2, 2).is_err());
80 /// ```
81 pub fn try_new(start: T, end: T) -> Result<Self, EmptyGrouping> {
82 let ret = Self { start, end };
83
84 if ret.is_empty() {
85 Err(EmptyGrouping)
86 } else {
87 Ok(ret)
88 }
89 }
90
91 /// Creates a new closed range, panicking if it would be empty.
92 ///
93 /// ```
94 /// use willow_data_model::prelude::*;
95 ///
96 /// let yay = ClosedRange::new(2, 7);
97 /// ```
98 ///
99 /// ```should_panic
100 /// use willow_data_model::prelude::*;
101 ///
102 /// let nope = ClosedRange::new(7, 2);
103 /// ```
104 pub fn new(start: T, end: T) -> Self {
105 Self::try_new(start, end).unwrap()
106 }
107
108 /// Returns whether the given value is included in `self`.
109 ///
110 /// ```
111 /// use willow_data_model::prelude::*;
112 ///
113 /// let r = ClosedRange::new(2, 7);
114 /// assert!(r.includes_value(&2));
115 /// assert!(!r.includes_value(&1));
116 /// assert!(!r.includes_value(&7));
117 /// ```
118 pub fn includes_value(&self, t: &T) -> bool {
119 self.start <= *t && self.end > *t
120 }
121
122 /// Returns whether the given value is included in `self` and `other`.
123 ///
124 /// ```
125 /// use willow_data_model::prelude::*;
126 ///
127 /// let r1 = ClosedRange::new(2, 7);
128 /// let r2 = ClosedRange::new(5, 9);
129 /// assert!(r1.includes_value_in_intersection(&r2, &5));
130 /// assert!(!r1.includes_value_in_intersection(&r2, &2));
131 /// assert!(!r1.includes_value_in_intersection(&r2, &7));
132 /// assert!(!r1.includes_value_in_intersection(&r2, &22));
133 /// ```
134 pub fn includes_value_in_intersection(&self, other: &Self, t: &T) -> bool {
135 self.includes_value(t) && other.includes_value(t)
136 }
137
138 /// Returns whether the given closed range is included in `self`.
139 ///
140 /// ```
141 /// use willow_data_model::prelude::*;
142 ///
143 /// let r = ClosedRange::new(2, 7);
144 /// assert!(r.includes_closed_range(&ClosedRange::new(3, 6)));
145 /// assert!(r.includes_closed_range(&ClosedRange::new(2, 5)));
146 /// assert!(r.includes_closed_range(&ClosedRange::new(3, 7)));
147 /// assert!(r.includes_closed_range(&ClosedRange::new(2, 7)));
148 /// assert!(!r.includes_closed_range(&ClosedRange::new(1, 7)));
149 /// assert!(!r.includes_closed_range(&ClosedRange::new(2, 8)));
150 /// assert!(!r.includes_closed_range(&ClosedRange::new(9, 14)));
151 /// ```
152 pub fn includes_closed_range(&self, other: &Self) -> bool {
153 self.start <= other.start && self.end >= other.end
154 }
155
156 /// Returns whether the given closed range is strictly included in `self`.
157 ///
158 /// ```
159 /// use willow_data_model::prelude::*;
160 ///
161 /// let r = ClosedRange::new(2, 7);
162 /// assert!(r.strictly_includes_closed_range(&ClosedRange::new(2, 6)));
163 /// assert!(r.strictly_includes_closed_range(&ClosedRange::new(3, 7)));
164 /// assert!(r.strictly_includes_closed_range(&ClosedRange::new(3, 6)));
165 /// assert!(!r.strictly_includes_closed_range(&ClosedRange::new(2, 7)));
166 /// assert!(!r.strictly_includes_closed_range(&ClosedRange::new(1, 7)));
167 /// assert!(!r.strictly_includes_closed_range(&ClosedRange::new(2, 8)));
168 /// assert!(!r.strictly_includes_closed_range(&ClosedRange::new(9, 14)));
169 /// ```
170 pub fn strictly_includes_closed_range(&self, other: &Self) -> bool {
171 self != other && self.includes_closed_range(other)
172 }
173
174 // Private because we never expose empty ranges.
175 fn is_empty(&self) -> bool {
176 self.start >= self.end
177 }
178}
179
180impl<T> ClosedRange<T>
181where
182 T: PartialOrd + Clone,
183{
184 /// Returns the intersection between `self` and `other`, or an [`EmptyGrouping`] error if it would be empty.
185 ///
186 /// assert_eq!(
187 /// ClosedRange::new(2, 7).intersection_closed_range(ClosedRange::new(5, 9)),
188 /// Ok(ClosedRange::new(5, 7)),
189 /// );
190 /// assert!(
191 /// ClosedRange::new(2, 5).intersection_closed_range(ClosedRange::new(7, 9)).is_error(),
192 /// );
193 pub fn intersection_closed_range(&self, other: &Self) -> Result<Self, EmptyGrouping> {
194 let start = match self.start.partial_cmp(other.start()) {
195 None => return Err(EmptyGrouping),
196 Some(Ordering::Less) => other.start().clone(),
197 Some(_) => self.start().clone(),
198 };
199
200 let end = match self.end.partial_cmp(other.end()) {
201 None => return Err(EmptyGrouping),
202 Some(Ordering::Greater) => other.end().clone(),
203 Some(_) => self.end().clone(),
204 };
205
206 Self::try_new(start, end)
207 }
208}
209
210impl<T> ClosedRange<T> {
211 /// Creates a new closed range, without enforcing non-emptyness.
212 ///
213 /// #### Safety
214 ///
215 /// Calling this method with `start >= end` is undefined behaviour.
216 ///
217 /// ```
218 /// use willow_data_model::prelude::*;
219 ///
220 /// let yay = unsafe { ClosedRange::new_unchecked(2, 7) };
221 /// ```
222 pub unsafe fn new_unchecked(start: T, end: T) -> Self {
223 Self { start, end }
224 }
225
226 /// Returns a reference to the start value of this closed range.
227 ///
228 /// ```
229 /// use willow_data_model::prelude::*;
230 ///
231 /// let r = ClosedRange::new(2, 7);
232 /// assert_eq!(r.start(), &2);
233 /// ```
234 pub fn start(&self) -> &T {
235 &self.start
236 }
237
238 /// Returns a reference to the end value of this closed range.
239 ///
240 /// ```
241 /// use willow_data_model::prelude::*;
242 ///
243 /// let r = ClosedRange::new(2, 7);
244 /// assert_eq!(r.end(), &7);
245 /// ```
246 pub fn end(&self) -> &T {
247 &self.end
248 }
249
250 /// Takes ownership of a closed range and returns its start and end value.
251 ///
252 /// ```
253 /// use willow_data_model::prelude::*;
254 ///
255 /// let r = ClosedRange::new(2, 7);
256 /// assert_eq!(r.into_components(), (2, 7));
257 /// ```
258 pub fn into_components(self) -> (T, T) {
259 (self.start, self.end)
260 }
261}
262
263impl<T> RangeBounds<T> for ClosedRange<T> {
264 fn start_bound(&self) -> Bound<&T> {
265 Bound::Included(&self.start)
266 }
267
268 fn end_bound(&self) -> Bound<&T> {
269 Bound::Excluded(&self.end)
270 }
271}
272
273#[cfg(feature = "dev")]
274impl<'a, T> Arbitrary<'a> for ClosedRange<T>
275where
276 T: Arbitrary<'a> + PartialOrd,
277{
278 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
279 let start = T::arbitrary(u)?;
280 let end = T::arbitrary(u)?;
281
282 Self::try_new(start, end).map_err(|_| arbitrary::Error::IncorrectFormat)
283 }
284
285 fn try_size_hint(
286 depth: usize,
287 ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
288 Ok(size_hint::and_all(&[
289 T::try_size_hint(depth)?,
290 T::try_size_hint(depth)?,
291 ]))
292 }
293}
294
295/// A non-empty continuous subset of a type implementing [`PartialOrd`].
296///
297/// It is either a [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) consisting of an inclusive start value and a strictly-greater exclusive end value, or an [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range) consisting only of an inclusive start value.
298///
299/// ```
300/// use std::ops::RangeBounds;
301/// use willow_data_model::prelude::*;
302///
303/// assert!(WillowRange::<u8>::new_closed(4, 9).contains(&6));
304/// assert!(!WillowRange::<u8>::new_closed(4, 9).contains(&12));
305/// ```
306#[derive(Clone, Copy, PartialEq, Eq, Hash)]
307#[repr(C)]
308pub enum WillowRange<T> {
309 /// A [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range) with an inclusive start value and an exclusive end value.
310 Closed(ClosedRange<T>),
311 /// An [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range) with an inclusive start value.
312 ///
313 /// It includes all values greater than or equal to that start value.
314 Open(T),
315}
316
317impl<T: Debug> Debug for WillowRange<T> {
318 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
319 match self {
320 Self::Closed(closed) => closed.fmt(f),
321 Self::Open(start) => {
322 start.fmt(f)?;
323 write!(f, "..")?;
324 Ok(())
325 }
326 }
327 }
328}
329
330impl<T> RangeBounds<T> for WillowRange<T> {
331 fn start_bound(&self) -> Bound<&T> {
332 match self {
333 Self::Closed(inner) => inner.start_bound(),
334 Self::Open(start) => Bound::Included(start),
335 }
336 }
337
338 fn end_bound(&self) -> Bound<&T> {
339 match self {
340 Self::Closed(inner) => inner.end_bound(),
341 Self::Open(_) => Bound::Unbounded,
342 }
343 }
344}
345
346#[cfg(feature = "dev")]
347impl<'a, T> Arbitrary<'a> for WillowRange<T>
348where
349 T: Arbitrary<'a> + PartialOrd,
350{
351 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
352 if bool::arbitrary(u)? {
353 Ok(Self::Closed(ClosedRange::<T>::arbitrary(u)?))
354 } else {
355 Ok(Self::Open(T::arbitrary(u)?))
356 }
357 }
358
359 fn try_size_hint(
360 depth: usize,
361 ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
362 Ok(size_hint::or(
363 size_hint::and_all(&[
364 bool::try_size_hint(depth)?,
365 ClosedRange::<T>::try_size_hint(depth)?,
366 ]),
367 size_hint::and(bool::try_size_hint(depth)?, T::try_size_hint(depth)?),
368 ))
369 }
370}
371
372impl<T> WillowRange<T> {
373 /// Creates a new open range.
374 ///
375 /// ```
376 /// use willow_data_model::prelude::*;
377 ///
378 /// let open = WillowRange::new_open(2);
379 ///
380 /// assert!(open.is_open());
381 /// assert_eq!(open.start(), &2);
382 /// ```
383 pub fn new_open(start: T) -> Self {
384 Self::Open(start)
385 }
386
387 /// Returns a reference to the start value of this range.
388 ///
389 /// ```
390 /// use willow_data_model::prelude::*;
391 ///
392 /// let closed = WillowRange::new_closed(2, 7);
393 /// assert_eq!(closed.start(), &2);
394 ///
395 /// let open = WillowRange::new_open(2);
396 /// assert_eq!(open.start(), &2);
397 /// ```
398 pub fn start(&self) -> &T {
399 match self {
400 Self::Closed(inner) => inner.start(),
401 Self::Open(start) => start,
402 }
403 }
404
405 /// Returns a reference to the end value of this range, or `None` of it is open.
406 ///
407 /// ```
408 /// use willow_data_model::prelude::*;
409 ///
410 /// let closed = WillowRange::new_closed(2, 7);
411 /// assert_eq!(closed.end(), Some(&7));
412 ///
413 /// let open = WillowRange::new_open(2);
414 /// assert_eq!(open.end(), None);
415 /// ```
416 pub fn end(&self) -> Option<&T> {
417 match self {
418 Self::Closed(inner) => Some(inner.end()),
419 Self::Open(_) => None,
420 }
421 }
422
423 /// Takes ownership of a willow range and returns its start and end.
424 ///
425 /// ```
426 /// use willow_data_model::prelude::*;
427 ///
428 /// let closed = WillowRange::new_closed(2, 7);
429 /// assert_eq!(closed.into_components(), (2, Some(7)));
430 ///
431 /// let open = WillowRange::new_open(2);
432 /// assert_eq!(open.into_components(), (2, None));
433 /// ```
434 pub fn into_components(self) -> (T, Option<T>) {
435 match self {
436 Self::Closed(inner) => (inner.start, Some(inner.end)),
437 Self::Open(start) => (start, None),
438 }
439 }
440
441 /// Returns whether `self` is a [closed range](https://willowprotocol.org/specs/grouping-entries/index.html#closed_range).
442 ///
443 /// ```
444 /// use willow_data_model::prelude::*;
445 ///
446 /// assert_eq!(WillowRange::<u8>::new_closed(3, 8).is_closed(), true);
447 /// assert_eq!(WillowRange::<u8>::new_open(3).is_closed(), false);
448 /// ```
449 pub fn is_closed(&self) -> bool {
450 matches!(self, Self::Closed(_))
451 }
452
453 /// Returns whether `self` is an [open range](https://willowprotocol.org/specs/grouping-entries/index.html#open_range).
454 ///
455 /// ```
456 /// use willow_data_model::prelude::*;
457 ///
458 /// assert_eq!(WillowRange::<u8>::new_closed(3, 8).is_open(), false);
459 /// assert_eq!(WillowRange::<u8>::new_open(3).is_open(), true);
460 /// ```
461 pub fn is_open(&self) -> bool {
462 matches!(self, Self::Open(_))
463 }
464
465 /// Converts this range into a different range by applying a function to all endpoints. Returns an error if the new range would be empty.
466 ///
467 /// ```
468 /// use willow_data_model::prelude::*;
469 ///
470 /// assert_eq!(
471 /// WillowRange::<u8>::new_closed(3, 8).try_map(|x| (x + 1) as u16),
472 /// Ok(WillowRange::<u16>::new_closed(4, 9)),
473 /// );
474 /// ```
475 pub fn try_map<U, F>(self, mut f: F) -> Result<WillowRange<U>, EmptyGrouping>
476 where
477 F: FnMut(T) -> U,
478 U: PartialOrd,
479 {
480 match self {
481 Self::Closed(ClosedRange { start, end }) => {
482 Ok(WillowRange::Closed(ClosedRange::try_new(f(start), f(end))?))
483 }
484 Self::Open(start) => Ok(WillowRange::Open(f(start))),
485 }
486 }
487
488 /// Converts this range into a different range by applying a function to all endpoints. Panics if the new range would be empty.
489 ///
490 /// ```
491 /// use willow_data_model::prelude::*;
492 ///
493 /// assert_eq!(
494 /// WillowRange::<u8>::new_closed(3, 8).map(|x| (x + 1) as u16),
495 /// WillowRange::<u16>::new_closed(4, 9),
496 /// );
497 /// ```
498 pub fn map<U, F>(self, mut f: F) -> WillowRange<U>
499 where
500 F: FnMut(T) -> U,
501 U: PartialOrd,
502 {
503 match self {
504 Self::Closed(ClosedRange { start, end }) => {
505 WillowRange::Closed(ClosedRange::new(f(start), f(end)))
506 }
507 Self::Open(start) => WillowRange::Open(f(start)),
508 }
509 }
510
511 /// Converts this range into a different range by applying a function to all endpoints, without checking if the resulting range would be empty.
512 ///
513 /// ## Safety
514 ///
515 /// Undefined behaviour may occur if the resulting range would be empty.
516 ///
517 /// ```
518 /// use willow_data_model::prelude::*;
519 ///
520 /// assert_eq!(
521 /// unsafe {
522 /// WillowRange::<u8>::new_closed(3, 8).map_unchecked(|x| (x + 1) as u16)
523 /// },
524 /// WillowRange::<u16>::new_closed(4, 9),
525 /// );
526 /// ```
527 pub unsafe fn map_unchecked<U, F>(self, mut f: F) -> WillowRange<U>
528 where
529 F: FnMut(T) -> U,
530 U: PartialOrd,
531 {
532 match self {
533 Self::Closed(ClosedRange { start, end }) => {
534 // SAFETY: the unsafety of this call is exactly why this method is unsafe itself.
535 WillowRange::Closed(unsafe { ClosedRange::new_unchecked(f(start), f(end)) })
536 }
537 Self::Open(start) => WillowRange::Open(f(start)),
538 }
539 }
540
541 /// Unsafely reinterprets a reference to a range as a reference to a range of a different type of boundaries.
542 ///
543 /// # Safety
544 ///
545 /// This is safe only if `&T` and `&U` have an identical layout in memory, and if the resulting range is non-empty.
546 pub unsafe fn cast<U>(&self) -> &WillowRange<U> {
547 // SAFETY: WillowRange and ClosedRanged are `repr(C)`, so if `T` and `U` have identical
548 // layout, then so do `WillowRange<T>` and `WillowRange<U>`
549 unsafe { &*(self as *const Self as *const WillowRange<U>) }
550 }
551
552 /// Creates a new Willow range, without enforcing non-emptyness.
553 ///
554 /// #### Safety
555 ///
556 /// Calling this method with `start >= end` is undefined behaviour.
557 ///
558 /// ```
559 /// use willow_data_model::prelude::*;
560 ///
561 /// let yay = unsafe { WillowRange::new_unchecked(2, Some(7)) };
562 /// ```
563 pub unsafe fn new_unchecked(start: T, end: Option<T>) -> Self {
564 match end {
565 // SAFETY: the unsafety of this call is exactly why this method is unsafe itself.
566 Some(end) => Self::Closed(unsafe { ClosedRange::new_unchecked(start, end) }),
567 None => Self::new_open(start),
568 }
569 }
570
571 /// Creates a new closed Willow range, without enforcing non-emptyness.
572 ///
573 /// #### Safety
574 ///
575 /// Calling this method with `start >= end` is undefined behaviour.
576 ///
577 /// ```
578 /// use willow_data_model::prelude::*;
579 ///
580 /// let yay = unsafe { WillowRange::new_closed_unchecked(2, 7) };
581 /// ```
582 pub unsafe fn new_closed_unchecked(start: T, end: T) -> Self {
583 // SAFETY: the unsafety of this call is exactly why this method is unsafe itself.
584 Self::Closed(unsafe { ClosedRange::new_unchecked(start, end) })
585 }
586}
587
588impl<T> WillowRange<T>
589where
590 T: PartialOrd,
591{
592 /// Creates a new Willow range from a start value and an optional end value, or returns an [`EmptyGrouping`] error if the created range would be empty.
593 ///
594 /// ```
595 /// use willow_data_model::prelude::*;
596 ///
597 /// assert!(WillowRange::try_new(2, Some(7)).is_ok());
598 /// assert!(WillowRange::try_new(7, Some(2)).is_err());
599 /// assert!(WillowRange::try_new(2, Some(2)).is_err());
600 /// ```
601 pub fn try_new(start: T, end: Option<T>) -> Result<Self, EmptyGrouping> {
602 let ret = match end {
603 Some(end) => Self::Closed(ClosedRange { start, end }),
604 None => Self::Open(start),
605 };
606
607 if ret.is_empty() {
608 Err(EmptyGrouping)
609 } else {
610 Ok(ret)
611 }
612 }
613
614 /// Creates a new Willow range from a start value and an optional end value, panicking if the created range would be empty.
615 ///
616 /// ```
617 /// use willow_data_model::prelude::*;
618 ///
619 /// let yay = WillowRange::new_closed(2, 7);
620 /// ```
621 ///
622 /// ```should_panic
623 /// use willow_data_model::prelude::*;
624 ///
625 /// let nope = WillowRange::new(7, Some(2));
626 /// ```
627 pub fn new(start: T, end: Option<T>) -> Self {
628 Self::try_new(start, end).unwrap()
629 }
630
631 /// Creates a new closed Willow range, or returns an [`EmptyGrouping`] error if the created range would be empty.
632 ///
633 /// ```
634 /// use willow_data_model::prelude::*;
635 ///
636 /// assert!(WillowRange::try_new_closed(2, 7).is_ok());
637 /// assert!(WillowRange::try_new_closed(7, 2).is_err());
638 /// assert!(WillowRange::try_new_closed(2, 2).is_err());
639 /// ```
640 pub fn try_new_closed(start: T, end: T) -> Result<Self, EmptyGrouping> {
641 Ok(Self::Closed(ClosedRange::try_new(start, end)?))
642 }
643
644 /// Creates a new Willow range from a start value and an optional end value, panicking if the created range would be empty.
645 ///
646 /// ```
647 /// use willow_data_model::prelude::*;
648 ///
649 /// let yay = WillowRange::new_closed(2, 7);
650 /// ```
651 ///
652 /// ```should_panic
653 /// use willow_data_model::prelude::*;
654 ///
655 /// let nope = WillowRange::new_closed(7, 2);
656 /// ```
657 pub fn new_closed(start: T, end: T) -> Self {
658 Self::Closed(ClosedRange::new(start, end))
659 }
660
661 /// Returns whether the given value is included in `self`.
662 ///
663 /// ```
664 /// use willow_data_model::prelude::*;
665 ///
666 /// let closed = WillowRange::new_closed(2, 7);
667 /// assert!(closed.includes_value(&2));
668 /// assert!(!closed.includes_value(&1));
669 /// assert!(!closed.includes_value(&7));
670 ///
671 /// let open = WillowRange::new_open(2);
672 /// assert!(open.includes_value(&2));
673 /// assert!(!open.includes_value(&1));
674 /// assert!(open.includes_value(&7));
675 /// ```
676 pub fn includes_value(&self, t: &T) -> bool {
677 self.start() <= t && self.end().map(|end| end > t).unwrap_or(true)
678 }
679
680 /// Returns whether the given value is included in `self` and `other`.
681 ///
682 /// ```
683 /// use willow_data_model::prelude::*;
684 ///
685 /// let r1 = WillowRange::new_closed(2, 7);
686 /// let r2 = WillowRange::new_open(5);
687 /// assert!(r1.includes_value_in_intersection(&r2, &5));
688 /// assert!(!r1.includes_value_in_intersection(&r2, &2));
689 /// assert!(!r1.includes_value_in_intersection(&r2, &7));
690 /// assert!(!r1.includes_value_in_intersection(&r2, &1));
691 /// assert!(!r1.includes_value_in_intersection(&r2, &22));
692 /// ```
693 pub fn includes_value_in_intersection(&self, other: &Self, t: &T) -> bool {
694 self.includes_value(t) && other.includes_value(t)
695 }
696
697 /// Returns whether the given Willow range is included in `self`.
698 ///
699 /// ```
700 /// use willow_data_model::prelude::*;
701 ///
702 /// let r = WillowRange::new_closed(2, 7);
703 /// assert!(r.includes_willow_range(&WillowRange::new_closed(2, 7)));
704 /// assert!(!r.includes_willow_range(&WillowRange::new_closed(1, 7)));
705 /// assert!(!r.includes_willow_range(&WillowRange::new_closed(2, 8)));
706 /// assert!(!r.includes_willow_range(&WillowRange::new_closed(9, 14)));
707 /// assert!(!r.includes_willow_range(&WillowRange::new_open(2)));
708 /// ```
709 pub fn includes_willow_range(&self, other: &Self) -> bool {
710 self.start() <= other.start()
711 && match (self.end(), other.end()) {
712 (None, None) => true,
713 (Some(_), None) => false,
714 (None, Some(_)) => true,
715 (Some(self_end), Some(other_end)) => self_end >= other_end,
716 }
717 }
718
719 /// Returns whether the given Willow range is strictly included in `self`.
720 ///
721 /// ```
722 /// use willow_data_model::prelude::*;
723 ///
724 /// let r = WillowRange::new_closed(2, 7);
725 /// assert!(r.strictly_includes_willow_range(&WillowRange::new_closed(2, 6)));
726 /// assert!(r.strictly_includes_willow_range(&WillowRange::new_closed(3, 7)));
727 /// assert!(r.strictly_includes_willow_range(&WillowRange::new_closed(3, 6)));
728 /// assert!(!r.strictly_includes_willow_range(&WillowRange::new_closed(2, 7)));
729 /// assert!(!r.strictly_includes_willow_range(&WillowRange::new_closed(1, 7)));
730 /// assert!(!r.strictly_includes_willow_range(&WillowRange::new_closed(2, 8)));
731 /// assert!(!r.strictly_includes_willow_range(&WillowRange::new_closed(9, 14)));
732 /// assert!(!r.strictly_includes_willow_range(&WillowRange::new_open(2)));
733 /// ```
734 pub fn strictly_includes_willow_range(&self, other: &Self) -> bool {
735 self != other && self.includes_willow_range(other)
736 }
737
738 /// Only use this internally to check whether the non-emptyness invariant would be upheld by something. Not a public method because we *never publicly expose empty ranges.
739 fn is_empty(&self) -> bool {
740 match self {
741 WillowRange::Closed(inner) => inner.is_empty(),
742 WillowRange::Open(_) => false,
743 }
744 }
745}
746
747impl<T> WillowRange<T>
748where
749 T: PartialOrd + Clone,
750{
751 /// Returns the intersection between `self` and `other`, or an [`EmptyGrouping`] error if it would be empty.
752 ///
753 /// assert_eq!(
754 /// WillowRange::new(2, 7).intersection_willow_range(WillowRange::new(5, 9)),
755 /// Ok(WillowRange::new(5, 7)),
756 /// );
757 /// assert!(
758 /// WillowRange::new(2, 5).intersection_willow_range(WillowRange::new(7, 9)).is_error(),
759 /// );
760 pub fn intersection_willow_range(&self, other: &Self) -> Result<Self, EmptyGrouping> {
761 let start = match self.start().partial_cmp(other.start()) {
762 None => return Err(EmptyGrouping),
763 Some(Ordering::Less) => other.start().clone(),
764 Some(_) => self.start().clone(),
765 };
766
767 let end = match (self.end(), other.end()) {
768 (None, None) => None,
769 (Some(start), None) | (None, Some(start)) => Some(start.clone()),
770 (Some(self_end), Some(other_end)) => match self_end.partial_cmp(other_end) {
771 None => return Err(EmptyGrouping),
772 Some(Ordering::Greater) => Some(other_end.clone()),
773 _ => Some(self_end.clone()),
774 },
775 };
776
777 Self::try_new(start, end)
778 }
779}
780
781/// A range is less than another iff all values contained in the first are also contained in the other.
782impl<T> PartialOrd for WillowRange<T>
783where
784 T: PartialOrd,
785{
786 fn partial_cmp(&self, other: &WillowRange<T>) -> Option<Ordering> {
787 match (self, other) {
788 (Self::Closed(self_closed), Self::Closed(other_closed)) => {
789 self_closed.partial_cmp(other_closed)
790 }
791 (Self::Closed(self_closed), Self::Open(other_open)) => {
792 match self_closed.start().partial_cmp(other_open)? {
793 Ordering::Less => None,
794 Ordering::Equal | Ordering::Greater => Some(Ordering::Less),
795 }
796 }
797 (Self::Open(self_open), Self::Closed(other_closed)) => {
798 match self_open.partial_cmp(other_closed.start())? {
799 Ordering::Greater => None,
800 Ordering::Equal | Ordering::Less => Some(Ordering::Greater),
801 }
802 }
803 (Self::Open(self_open), Self::Open(other_open)) => {
804 match self_open.partial_cmp(other_open)? {
805 Ordering::Less => Some(Ordering::Greater),
806 Ordering::Equal => Some(Ordering::Equal),
807 Ordering::Greater => Some(Ordering::Less),
808 }
809 }
810 }
811 }
812}
813
814impl<T> WillowRange<T>
815where
816 T: SuccessorExceptForGreatest,
817{
818 /// Returns the [`WillowRange`] which [includes](core::ops::RangeBounds::contains) `t` but no other value.
819 ///
820 /// Panicks if `T::try_successor` returns a valid which is not greater than `t`.
821 ///
822 /// ```
823 /// use willow_data_model::prelude::*;
824 ///
825 /// assert_eq!(WillowRange::<u8>::singleton(5), WillowRange::<u8>::new_closed(5, 6));
826 /// assert_eq!(WillowRange::<u8>::singleton(255), WillowRange::<u8>::new_open(255));
827 /// ```
828 pub fn singleton(t: T) -> Self {
829 match t.try_successor() {
830 Some(succ) => Self::Closed(ClosedRange::new(t, succ)),
831 None => Self::Open(t),
832 }
833 }
834}
835
836impl<T> WillowRange<T>
837where
838 T: LeastElement,
839{
840 /// Returns the `WillowRange` which [includes](core::ops::RangeBounds::contains) every value of type `T`.
841 ///
842 /// ```
843 /// use willow_data_model::prelude::*;
844 ///
845 /// assert_eq!(WillowRange::<u8>::full(), WillowRange::<u8>::new_open(0));
846 /// ```
847 pub fn full() -> Self {
848 Self::Open(T::least())
849 }
850
851 /// Returns whether `self` is the full range, i.e., the range which [includes](core::ops::RangeBounds::contains) every value of type `T`.
852 ///
853 /// ```
854 /// use willow_data_model::prelude::*;
855 ///
856 /// assert_eq!(WillowRange::<u8>::full().is_full(), true);
857 /// assert_eq!(WillowRange::<u8>::new_open(1).is_full(), false);
858 /// ```
859 pub fn is_full(&self) -> bool {
860 match self {
861 Self::Closed(_) => false,
862 Self::Open(start) => start.is_least(),
863 }
864 }
865}
866
867impl<T> GreatestElement for WillowRange<T>
868where
869 T: LeastElement,
870{
871 fn greatest() -> Self {
872 Self::Open(T::least())
873 }
874}