use core::{borrow::Borrow, ops::RangeBounds, str};
use super::{
bytes::{RawBytes, strategy::ImmutableStorage, visible_subslice_range},
error::*,
utf8_buf::{Utf8Buf, range_bounds_to_start_end, sealed, validate_char_boundary},
};
mod cmp;
mod fmt;
mod from;
mod iter;
mod ops;
#[cfg(feature = "arbitrary")]
mod arbitrary;
#[cfg(feature = "borsh")]
mod borsh;
#[cfg(feature = "quickcheck")]
mod quickcheck;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "pyo3")]
mod python;
#[cfg(feature = "pyo3")]
pub use python::PyCompactUtf8Bytes;
#[cfg(feature = "pyo3")]
pub use python::PySharedUtf8Bytes;
#[cfg(feature = "wasm")]
pub(crate) mod wasm;
pub struct Utf8Bytes<S> {
pub(crate) inner: RawBytes<S>,
}
impl<S> Clone for Utf8Bytes<S> {
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<S> sealed::Sealed for Utf8Bytes<S> {}
impl<S> Default for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn default() -> Self {
Self::new()
}
}
impl<S> Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
pub const fn new() -> Self {
Self {
inner: RawBytes::new(),
}
}
pub const fn from_static(s: &'static str) -> Self {
Self {
inner: RawBytes::from_static(s.as_bytes()),
}
}
pub fn from_utf8(bytes: RawBytes<S>) -> Result<Self, FromUtf8Error<RawBytes<S>>> {
match core::str::from_utf8(bytes.as_ref()) {
Ok(_) => Ok(Self { inner: bytes }),
Err(error) => Err(FromUtf8Error::new(bytes, error)),
}
}
pub fn from_utf8_lossy(bytes: RawBytes<S>) -> Self {
match Self::from_utf8(bytes) {
Ok(value) => value,
Err(error) => Self::from(std::string::String::from_utf8_lossy(error.as_bytes()).into_owned()),
}
}
pub const fn as_inner(&self) -> &RawBytes<S> {
&self.inner
}
pub fn into_inner(self) -> RawBytes<S> {
self.inner
}
pub fn try_into_mut(self) -> Result<crate::Utf8BytesMut, Self> {
match self.inner.try_into_mut() {
Ok(inner) => Ok(crate::Utf8BytesMut { inner }),
Err(inner) => Err(Self { inner }),
}
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(self.inner.as_ref()) }
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[inline]
pub fn is_inline(&self) -> bool {
self.inner.is_inline()
}
#[inline]
pub fn is_heap(&self) -> bool {
self.inner.is_heap()
}
#[inline]
pub fn is_unique(&self) -> bool {
self.inner.is_unique()
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear();
}
pub fn truncate(&mut self, new_len: usize) {
self
.try_truncate(new_len)
.expect("new_len must lie on a UTF-8 character boundary");
}
pub fn try_truncate(&mut self, new_len: usize) -> Result<(), Utf8IndexError> {
if new_len >= self.len() {
return Ok(());
}
validate_char_boundary(self.as_str(), new_len)?;
self.inner.truncate(new_len);
Ok(())
}
pub fn pop(&mut self) -> Option<char> {
let ch = self.as_str().chars().next_back()?;
self.truncate(self.len() - ch.len_utf8());
Some(ch)
}
pub fn advance(&mut self, cnt: usize) {
self.try_advance(cnt).expect("advance failed");
}
pub fn try_advance(&mut self, cnt: usize) -> Result<(), Utf8IndexError> {
validate_char_boundary(self.as_str(), cnt)?;
self.inner.try_advance(cnt).expect("already checked bounds");
Ok(())
}
pub fn split_to(&mut self, at: usize) -> Self {
self.try_split_to(at).expect("split_to failed")
}
pub fn try_split_to(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
<Self as Utf8Buf>::try_split_to(self, at)
}
pub fn split_off(&mut self, at: usize) -> Self {
self.try_split_off(at).expect("split_off failed")
}
pub fn try_split_off(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
<Self as Utf8Buf>::try_split_off(self, at)
}
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
self.try_slice(range).expect("slice failed")
}
pub fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, Utf8IndexError> {
<Self as Utf8Buf>::try_slice(self, range)
}
pub fn slice_ref(&self, subset: &str) -> Self {
self
.try_slice_ref(subset)
.expect("slice_ref subset must be contained in the current visible view")
}
pub fn try_slice_ref(&self, subset: &str) -> Option<Self> {
if subset.is_empty() {
return Some(Self::new());
}
let range = visible_subslice_range(self.as_str().as_bytes(), subset.as_bytes())?;
self.try_slice(range).ok()
}
}
impl<S> AsRef<str> for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<S> AsRef<[u8]> for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl<S> Borrow<str> for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<S> core::ops::Deref for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[cfg(feature = "std")]
impl<S> AsRef<std::ffi::OsStr> for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn as_ref(&self) -> &std::ffi::OsStr {
std::ffi::OsStr::new(self.as_str())
}
}
#[cfg(feature = "std")]
impl<S> AsRef<std::path::Path> for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn as_ref(&self) -> &std::path::Path {
std::path::Path::new(self.as_str())
}
}
impl<S> Utf8Buf for Utf8Bytes<S>
where
RawBytes<S>: ImmutableStorage,
{
fn try_split_to(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
validate_char_boundary(self.as_str(), at)?;
Ok(Self {
inner: self.inner.try_split_to(at).expect("already checked bounds"),
})
}
fn try_split_off(&mut self, at: usize) -> Result<Self, Utf8IndexError> {
validate_char_boundary(self.as_str(), at)?;
Ok(Self {
inner: self
.inner
.try_split_off(at)
.expect("already checked bounds"),
})
}
fn try_slice(&self, range: impl RangeBounds<usize>) -> Result<Self, Utf8IndexError> {
let (start, end) = range_bounds_to_start_end(self.as_str(), range)?;
Ok(Self {
inner: self
.inner
.try_slice(start..end)
.expect("already checked bounds"),
})
}
}