use core::ops::RangeBounds;
use super::error::{Utf8Error, normalize_range};
pub trait Utf8Buf: AsRef<str> + Sized {
fn as_str(&self) -> &str {
self.as_ref()
}
fn len(&self) -> usize {
self.as_str().len()
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn split_to(&mut self, at: usize) -> Self {
self.try_split_to(at).expect("split_to failed")
}
fn try_split_to(&mut self, at: usize) -> Result<Self, Utf8Error>;
fn split_off(&mut self, at: usize) -> Self {
self.try_split_off(at).expect("split_off failed")
}
fn try_split_off(&mut self, at: usize) -> Result<Self, Utf8Error>;
fn slice(&self, range: impl RangeBounds<usize>) -> Self {
self.try_slice(range).expect("slice failed")
}
fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, Utf8Error>;
fn range_bounds_to_start_end(
&self,
range: impl RangeBounds<usize>,
) -> Result<(usize, usize), Utf8Error> {
let len = self.len();
let (start, end) =
normalize_range(range, len).map_err(|err| Utf8Error::OutOfBounds { at: err.end, len })?;
let s = self.as_str();
if !s.is_char_boundary(start) {
return Err(Utf8Error::InvalidCharBoundary { at: start });
}
if !s.is_char_boundary(end) {
return Err(Utf8Error::InvalidCharBoundary { at: end });
}
Ok((start, end))
}
fn validate_char_boundary(&self, at: usize) -> Result<(), Utf8Error> {
let len = self.len();
if at > len {
return Err(Utf8Error::OutOfBounds { at, len });
}
if !self.as_str().is_char_boundary(at) {
return Err(Utf8Error::InvalidCharBoundary { at });
}
Ok(())
}
}
pub trait Utf8BufMut: Utf8Buf {
fn try_truncate(&mut self, new_len: usize) -> Result<(), Utf8Error>;
fn push(&mut self, ch: char);
fn push_str(&mut self, s: &str);
fn clear(&mut self);
}
#[allow(dead_code)]
pub(crate) fn char_offset_to_byte(s: &str, chars: usize) -> Option<usize> {
if chars == s.chars().count() {
Some(s.len())
} else {
s.char_indices().nth(chars).map(|(index, _)| index)
}
}
#[cfg(test)]
mod char_offset_tests {
use super::char_offset_to_byte;
#[test]
fn maps_scalar_offset_to_byte_offset() {
assert_eq!(char_offset_to_byte("abc", 1), Some(1));
assert_eq!(char_offset_to_byte("é", 1), Some(2));
assert_eq!(char_offset_to_byte("aé", 1), Some(1));
assert_eq!(char_offset_to_byte("aé", 2), Some(3));
assert_eq!(char_offset_to_byte("", 0), Some(0));
assert_eq!(char_offset_to_byte("a", 2), None);
assert_eq!(char_offset_to_byte("🦀x", 1), Some(4));
}
}