use core::ops::{Bound, RangeBounds};
#[macro_export]
macro_rules! assert_str_range {
($s:expr, $r:expr) => {{
let s = &$s;
let r = &$r;
if !$crate::str::is_valid_range(s, r) {
$crate::str::range_fail(s, r)
}
}};
}
pub fn is_valid_range<S, R>(s: S, range: &R) -> bool
where
S: AsRef<str>,
R: RangeBounds<usize>,
{
let s = s.as_ref();
validate_start_bound(s, range.start_bound()).is_ok()
&& validate_end_bound(s, range.end_bound()).is_ok()
}
enum InvalidBound {
OutOfBuffer,
NotCharBoundary,
}
#[inline]
fn validate_start_bound(
s: &str,
bound: Bound<&usize>,
) -> Result<(), InvalidBound> {
use Bound::*;
match bound {
Unbounded => Ok(()),
Included(index) => validate_index(s, *index),
Excluded(index) => validate_next_index(s, *index),
}
}
#[inline]
fn validate_end_bound(
s: &str,
bound: Bound<&usize>,
) -> Result<(), InvalidBound> {
use Bound::*;
match bound {
Unbounded => Ok(()),
Excluded(index) => validate_index(s, *index),
Included(index) => validate_next_index(s, *index),
}
}
#[inline]
fn validate_index(s: &str, index: usize) -> Result<(), InvalidBound> {
use InvalidBound::*;
if s.is_char_boundary(index) {
Ok(())
} else if index > s.len() {
Err(OutOfBuffer)
} else {
Err(NotCharBoundary)
}
}
#[inline]
fn validate_next_index(s: &str, index: usize) -> Result<(), InvalidBound> {
use InvalidBound::*;
if index >= s.len() {
Err(OutOfBuffer)
} else if s.is_char_boundary(index + 1) {
Ok(())
} else {
Err(NotCharBoundary)
}
}
#[doc(hidden)]
#[cold]
pub fn range_fail<S, R>(s: S, range: &R) -> !
where
S: AsRef<str>,
R: RangeBounds<usize>,
{
range_fail_internal(s.as_ref(), range.start_bound(), range.end_bound())
}
fn range_fail_internal(
s: &str,
start_bound: Bound<&usize>,
end_bound: Bound<&usize>,
) -> ! {
use InvalidBound::*;
let start_validity = validate_start_bound(s, start_bound);
let end_validity = validate_end_bound(s, end_bound);
let r = (start_bound, end_bound);
match (start_validity, end_validity) {
(Err(OutOfBuffer), _) | (_, Err(OutOfBuffer)) => {
panic!("range {:?} is out of bounds", r)
}
(Err(NotCharBoundary), _) | (_, Err(NotCharBoundary)) => {
panic!("range {:?} does not split on a UTF-8 boundary", r)
}
(Ok(()), Ok(())) => unreachable!("there was no problem with the range"),
}
}