1use std::ops::{
2 Bound::{Excluded, Included, Unbounded},
3 Range, RangeBounds,
4};
5
6#[derive(Debug)]
7pub enum UsizeOob {
8 StartIndexLen(usize, usize, usize),
9 EndIndexLen(usize, usize, usize),
10 IndexOrder(usize, usize, usize),
11}
12
13impl ToString for UsizeOob {
14 fn to_string(&self) -> String {
15 match self {
16 UsizeOob::StartIndexLen(start, _, len) => {
17 format!(
18 "range start index {start} out of range for slice of length {len}"
19 )
20 }
21 UsizeOob::EndIndexLen(_, end, len) => {
22 format!(
23 "range end index {end} out of range for slice of length {len}"
24 )
25 }
26 UsizeOob::IndexOrder(start, end, _) => {
27 format!("slice index starts at {start} but ends at {end}")
28 }
29 }
30 }
31}
32
33impl UsizeOob {
34 pub fn resolve_bounds(&self) -> Range<usize> {
35 let (&start, &end, &len) = match self {
36 UsizeOob::StartIndexLen(start, end, len)
37 | UsizeOob::EndIndexLen(start, end, len)
38 | UsizeOob::IndexOrder(start, end, len) => (start, end, len),
39 };
40 let end = end.min(len);
41 let start = start.min(end);
42 start..end
43 }
44}
45
46pub trait UsizeBounds {
47 fn to_range(&self, len: usize) -> Range<usize>;
48 fn checked_to_range(&self, len: usize) -> Result<Range<usize>, UsizeOob>;
49}
50
51impl<R: RangeBounds<usize>> UsizeBounds for R {
52 fn to_range(&self, len: usize) -> Range<usize> {
53 match self.checked_to_range(len) {
54 Ok(o) => o,
55 Err(e) => panic!("{}", e.to_string()),
56 }
57 }
58
59 fn checked_to_range(&self, len: usize) -> Result<Range<usize>, UsizeOob> {
60 let start = match self.start_bound() {
61 Included(&s) => s,
62 Excluded(&s) => s + 1,
63 Unbounded => 0,
64 };
65 let end = match self.end_bound() {
66 Included(&e) => e + 1,
67 Excluded(&e) => e,
68 Unbounded => len,
69 };
70
71 if start > len {
72 Err(UsizeOob::StartIndexLen(start, end, len))
73 } else if end > len {
74 Err(UsizeOob::EndIndexLen(start, end, len))
75 } else if start > end {
76 Err(UsizeOob::IndexOrder(start, end, len))
77 } else {
78 Ok(start..end)
80 }
81 }
82}