use crate::{ReadText, TextReader, TextSubstr, TextWriter};
use basic_text_internals::unicode::{BOM, CGJ, WJ};
use basic_text_internals::{is_basic_text, is_basic_text_start};
use layered_io::Bufferable;
use std::borrow::{Borrow, BorrowMut, Cow};
use std::cmp::Ordering;
#[cfg(try_reserve)]
use std::collections::TryReserveError;
use std::error::Error;
use std::ffi::OsStr;
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::Hash;
use std::io::{self, Read, Write};
use std::net::{SocketAddr, ToSocketAddrs};
use std::ops::{Add, AddAssign, Deref, DerefMut, Index, Range, RangeFrom, RangeTo};
use std::path::Path;
#[cfg(pattern)]
use std::str::pattern::{Pattern, ReverseSearcher};
use std::str::{
self, Bytes, CharIndices, Chars, EncodeUtf16, EscapeDebug, EscapeDefault, EscapeUnicode,
FromStr, Lines, MatchIndices, Matches, RMatchIndices, RMatches, Utf8Error,
};
use std::string::FromUtf8Error;
use std::vec;
use utf8_io::WriteStr;
#[derive(PartialEq, Eq, Hash, Debug, Default)]
#[repr(transparent)]
pub struct TextString(pub(crate) String);
#[derive(PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
pub struct TextStr(pub(crate) str);
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct TextError {
pub(crate) valid_up_to: usize,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FromTextError {
pub(crate) bytes: Vec<u8>,
pub(crate) error: TextError,
}
impl TextString {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self(String::new())
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self(String::with_capacity(capacity))
}
#[inline]
pub fn from_text_vec(vec: Vec<u8>) -> Result<Self, FromTextError> {
Self::from_text(String::from_utf8(vec)?)
}
#[inline]
pub fn from_text(s: String) -> Result<Self, FromTextError> {
let bytes: Vec<u8> = Vec::new();
let mut writer = TextWriter::new(bytes);
match writer.write_str(&s).and_then(|()| writer.flush()) {
Ok(()) => (),
Err(_err) => {
writer.abandon();
let valid_up_to = compute_valid_up_to(&s);
return Err(FromTextError {
bytes: s.into_bytes(),
error: TextError { valid_up_to },
});
}
}
Ok(unsafe {
Self::from_text_vec_unchecked(
writer
.abandon_into_inner()
.abandon_into_inner()
.abandon_into_inner()
.unwrap(),
)
})
}
#[inline]
#[must_use]
pub fn from_text_bytes_lossy(v: &[u8]) -> Cow<TextStr> {
Cow::Owned(Self::from_text_lossy(&String::from_utf8_lossy(v)).into_owned())
}
#[inline]
#[must_use]
pub fn from_text_lossy(mut v: &str) -> Cow<TextStr> {
let mut reader = TextReader::new(v.as_bytes());
let mut text = String::new();
reader.read_to_string(&mut text).unwrap();
if let Some(suffix) = v.strip_prefix(BOM) {
text.insert(0, WJ);
v = suffix;
}
if !v.is_empty() && !v.ends_with(|c| matches!(c, '\n' | '\r')) {
let c = text.pop();
assert_eq!(c.unwrap(), '\n');
}
Cow::Owned(unsafe { Self::from_text_unchecked(text) })
}
#[inline]
#[must_use]
pub unsafe fn from_text_vec_unchecked(vec: Vec<u8>) -> Self {
Self::from_text_unchecked(String::from_utf8_unchecked(vec))
}
#[inline]
#[must_use]
pub const unsafe fn from_text_unchecked(s: String) -> Self {
Self(s)
}
#[inline]
#[must_use]
pub fn into_string(self) -> String {
self.0
}
#[inline]
#[must_use]
pub fn into_bytes(self) -> Vec<u8> {
self.0.into_bytes()
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[inline]
#[must_use]
pub fn as_text(&self) -> &TextStr {
self
}
#[inline]
#[must_use]
pub fn as_mut_text(&mut self) -> &mut TextStr {
self
}
#[inline]
pub fn push_text(&mut self, s: &TextStr) {
if let Some(rest) = s.0.strip_prefix(CGJ) {
if self.0.ends_with(CGJ) {
self.0.push_str(rest);
return;
}
}
self.0.push_str(&s.0);
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.0.reserve(additional);
}
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.0.reserve_exact(additional);
}
#[cfg(try_reserve)]
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.0.try_reserve(additional)
}
#[cfg(try_reserve)]
#[inline]
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.0.try_reserve_exact(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit();
}
#[cfg(shrink_to)]
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.0.shrink_to(min_capacity);
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
self.0.as_mut_vec()
}
#[inline]
pub unsafe fn as_mut_string(&mut self) -> &mut String {
&mut self.0
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
pub fn into_boxed_str(self) -> Box<str> {
self.0.into_boxed_str()
}
#[inline]
pub fn into_boxed_text(self) -> Box<TextStr> {
let slice = self.into_boxed_str();
unsafe { TextStr::from_boxed_text_unchecked(slice) }
}
}
#[cold]
fn compute_valid_up_to(s: &str) -> usize {
let mut begin = 0;
let mut end = s.len();
while begin != end {
let mut mid = begin + (end - begin) / 2;
while !s.is_char_boundary(mid) {
mid -= 1;
}
if mid == begin {
mid = begin + (end - begin) / 2 + 1;
while !s.is_char_boundary(mid) {
mid += 1;
}
if mid == end {
break;
}
}
let substr = &s[..mid];
if is_basic_text(substr) {
begin = mid;
} else {
end = mid;
}
}
begin
}
impl AsRef<[u8]> for TextString {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<OsStr> for TextString {
#[inline]
fn as_ref(&self) -> &OsStr {
let s: &str = self.as_ref();
s.as_ref()
}
}
impl AsRef<Path> for TextString {
#[inline]
fn as_ref(&self) -> &Path {
let s: &str = self.as_ref();
s.as_ref()
}
}
impl AsRef<str> for TextString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<TextStr> for TextString {
#[inline]
fn as_ref(&self) -> &TextStr {
self
}
}
impl AsRef<TextSubstr> for TextString {
#[inline]
fn as_ref(&self) -> &TextSubstr {
unsafe { TextSubstr::from_text_unchecked(&self.0) }
}
}
impl Clone for TextString {
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Deref for TextString {
type Target = TextStr;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { TextStr::from_text_unchecked(&self.0) }
}
}
impl DerefMut for TextString {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { TextStr::from_text_unchecked_mut(&mut self.0) }
}
}
impl Borrow<TextStr> for TextString {
#[inline]
fn borrow(&self) -> &TextStr {
self
}
}
impl BorrowMut<TextStr> for TextString {
#[inline]
fn borrow_mut(&mut self) -> &mut TextStr {
self
}
}
impl AsMut<TextStr> for TextString {
#[inline]
fn as_mut(&mut self) -> &mut TextStr {
self
}
}
impl Add<&TextStr> for TextString {
type Output = Self;
#[inline]
fn add(mut self, other: &TextStr) -> Self::Output {
self.push_text(other);
self
}
}
impl AddAssign<&TextStr> for TextString {
#[inline]
fn add_assign(&mut self, other: &TextStr) {
self.push_text(other);
}
}
impl Extend<TextString> for TextString {
fn extend<I: IntoIterator<Item = TextString>>(&mut self, iter: I) {
iter.into_iter().for_each(move |s| self.push_text(&s));
}
#[cfg(extend_one)]
#[inline]
fn extend_one(&mut self, s: TextString) {
self.push_text(&s);
}
}
impl<'a> Extend<&'a TextStr> for TextString {
fn extend<I: IntoIterator<Item = &'a TextStr>>(&mut self, iter: I) {
iter.into_iter().for_each(move |s| self.push_text(s));
}
#[cfg(extend_one)]
#[inline]
fn extend_one(&mut self, s: &'a TextStr) {
self.push_text(s);
}
}
impl<'a> Extend<Cow<'a, TextStr>> for TextString {
fn extend<I: IntoIterator<Item = Cow<'a, TextStr>>>(&mut self, iter: I) {
iter.into_iter().for_each(move |s| self.push_text(&s));
}
#[cfg(extend_one)]
#[inline]
fn extend_one(&mut self, s: Cow<'a, TextStr>) {
self.push_text(&s);
}
}
impl TextStr {
#[inline]
pub fn from_text_bytes(b: &[u8]) -> Result<&Self, TextError> {
Self::from_text(str::from_utf8(b)?)
}
#[inline]
pub fn from_text(s: &str) -> Result<&Self, TextError> {
if !is_basic_text(s) {
let valid_up_to = compute_valid_up_to(s);
return Err(TextError { valid_up_to });
}
Ok(unsafe { Self::from_text_unchecked(s) })
}
#[inline]
pub fn from_text_bytes_mut(b: &mut [u8]) -> Result<&mut Self, TextError> {
Self::from_text_mut(str::from_utf8_mut(b)?)
}
#[inline]
pub fn from_text_mut(s: &mut str) -> Result<&mut Self, TextError> {
if !is_basic_text(s) {
let valid_up_to = compute_valid_up_to(s);
return Err(TextError { valid_up_to });
}
Ok(unsafe { Self::from_text_unchecked_mut(s) })
}
#[inline]
pub unsafe fn from_text_bytes_unchecked(b: &[u8]) -> &Self {
Self::from_text_unchecked(str::from_utf8_unchecked(b))
}
#[inline]
pub unsafe fn from_text_unchecked(s: &str) -> &Self {
let ptr: *const str = s;
&*(ptr as *const Self)
}
#[inline]
pub unsafe fn from_text_bytes_unchecked_mut(b: &mut [u8]) -> &mut Self {
Self::from_text_unchecked_mut(str::from_utf8_unchecked_mut(b))
}
#[inline]
pub unsafe fn from_text_unchecked_mut(s: &mut str) -> &mut Self {
let ptr: *mut str = s;
&mut *(ptr as *mut Self)
}
#[inline]
pub unsafe fn from_boxed_text_bytes_unchecked(v: Box<[u8]>) -> Box<Self> {
let ptr = Box::into_raw(v);
Box::from_raw(ptr as *mut Self)
}
#[inline]
pub unsafe fn from_boxed_text_unchecked(v: Box<str>) -> Box<Self> {
let ptr = Box::into_raw(v);
Box::from_raw(ptr as *mut Self)
}
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
self.0.as_bytes_mut()
}
#[inline]
pub const fn as_ptr(&self) -> *const u8 {
self.0.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.0.as_mut_ptr()
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
#[inline]
pub fn split_at(&self, mid: usize) -> (&Self, &Self) {
let (prefix, rest) = self.0.split_at(mid);
(
Self::from_text(prefix).unwrap(),
Self::from_text(rest).unwrap(),
)
}
#[inline]
pub fn split_at_mut(&mut self, mid: usize) -> (&mut Self, &mut Self) {
let (prefix, rest) = self.0.split_at_mut(mid);
(
Self::from_text_mut(prefix).unwrap(),
Self::from_text_mut(rest).unwrap(),
)
}
#[inline]
pub fn chars(&self) -> Chars {
self.0.chars()
}
#[inline]
pub fn char_indices(&self) -> CharIndices {
self.0.char_indices()
}
#[inline]
pub fn bytes(&self) -> Bytes {
self.0.bytes()
}
#[inline]
pub fn lines(&self) -> Lines {
self.0.lines()
}
#[inline]
pub fn encode_utf16(&self) -> EncodeUtf16<'_> {
self.0.encode_utf16()
}
#[cfg(pattern)]
#[inline]
pub fn contains<'a, P>(&'a self, pat: P) -> bool
where
P: Pattern<'a>,
{
self.0.contains(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn contains<'a>(&'a self, pat: &str) -> bool {
self.0.contains(pat)
}
#[cfg(pattern)]
#[inline]
pub fn starts_with<'a, P>(&'a self, pat: P) -> bool
where
P: Pattern<'a>,
{
self.0.starts_with(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn starts_with<'a>(&'a self, pat: &str) -> bool {
self.0.starts_with(pat)
}
#[cfg(pattern)]
#[inline]
pub fn ends_with<'a, P>(&'a self, pat: P) -> bool
where
P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
{
self.0.ends_with(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn ends_with<'a>(&'a self, pat: &str) -> bool {
self.0.ends_with(pat)
}
#[cfg(pattern)]
#[inline]
pub fn find<'a, P>(&'a self, pat: P) -> Option<usize>
where
P: Pattern<'a>,
{
self.0.find(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn find<'a>(&'a self, pat: &str) -> Option<usize> {
self.0.find(pat)
}
#[cfg(pattern)]
#[inline]
pub fn rfind<'a, P>(&'a self, pat: P) -> Option<usize>
where
P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
{
self.0.rfind(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn rfind<'a>(&'a self, pat: &str) -> Option<usize> {
self.0.rfind(pat)
}
#[cfg(pattern)]
#[inline]
pub fn matches<'a, P>(&'a self, pat: P) -> Matches<'a, P>
where
P: Pattern<'a>,
{
self.0.matches(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn matches<'a>(&'a self, pat: &'a str) -> Matches<'a, &str> {
self.0.matches(pat)
}
#[cfg(pattern)]
#[inline]
pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P>
where
P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
{
self.0.rmatches(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn rmatches<'a>(&'a self, pat: &'a str) -> RMatches<'a, &'a str> {
self.0.rmatches(pat)
}
#[cfg(pattern)]
#[inline]
pub fn match_indices<'a, P>(&'a self, pat: P) -> MatchIndices<'a, P>
where
P: Pattern<'a>,
{
self.0.match_indices(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn match_indices<'a>(&'a self, pat: &'a str) -> MatchIndices<'a, &'a str> {
self.0.match_indices(pat)
}
#[cfg(pattern)]
#[inline]
pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P>
where
P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>,
{
self.0.rmatch_indices(pat)
}
#[cfg(not(pattern))]
#[inline]
pub fn rmatch_indices<'a>(&'a self, pat: &'a str) -> RMatchIndices<'a, &'a str> {
self.0.rmatch_indices(pat)
}
#[inline]
pub fn trim(&self) -> &Self {
unsafe { Self::from_text_unchecked(self.0.trim()) }
}
#[inline]
pub fn trim_start(&self) -> &Self {
unsafe { Self::from_text_unchecked(self.0.trim_start()) }
}
#[inline]
pub fn trim_end(&self) -> &Self {
unsafe { Self::from_text_unchecked(self.0.trim_end()) }
}
#[inline]
pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err>
where
F: FromStr,
{
self.0.parse()
}
#[inline]
pub fn is_ascii(&self) -> bool {
self.0.is_ascii()
}
#[inline]
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
self.0.eq_ignore_ascii_case(&other.0)
}
#[inline]
pub fn into_boxed_bytes(self: Box<Self>) -> Box<[u8]> {
self.into_boxed_text().into_boxed_bytes()
}
#[inline]
pub fn into_boxed_text(self: Box<Self>) -> Box<str> {
self.into()
}
#[inline]
pub fn into_string(self: Box<Self>) -> String {
let slice = Box::<[u8]>::from(self);
unsafe { String::from_utf8_unchecked(slice.into_vec()) }
}
#[inline]
pub fn into_text_string(self: Box<Self>) -> TextString {
unsafe { TextString::from_text_unchecked(Self::into_string(self)) }
}
pub fn repeat(&self, n: usize) -> TextString {
unsafe { TextString::from_text_vec_unchecked(self.as_bytes().repeat(n)) }
}
#[inline]
pub fn escape_debug(&self) -> EscapeDebug<'_> {
self.0.escape_debug()
}
#[inline]
pub fn escape_default(&self) -> EscapeDefault<'_> {
self.0.escape_default()
}
#[inline]
pub fn escape_unicode(&self) -> EscapeUnicode {
self.0.escape_unicode()
}
}
impl AsRef<[u8]> for TextStr {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl AsRef<OsStr> for TextStr {
#[inline]
fn as_ref(&self) -> &OsStr {
self.0.as_ref()
}
}
impl AsRef<Path> for TextStr {
#[inline]
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl AsRef<str> for TextStr {
#[inline]
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<Self> for TextStr {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<TextSubstr> for TextStr {
#[inline]
fn as_ref(&self) -> &TextSubstr {
unsafe { TextSubstr::from_text_unchecked(&self.0) }
}
}
impl Default for &TextStr {
#[inline]
fn default() -> Self {
unsafe { TextStr::from_text_unchecked("") }
}
}
impl Default for &mut TextStr {
#[inline]
fn default() -> Self {
unsafe { TextStr::from_text_bytes_unchecked_mut(&mut []) }
}
}
impl Display for TextStr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Ord for TextStr {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd<Self> for TextStr {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl PartialOrd<Self> for TextString {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl ToOwned for TextStr {
type Owned = TextString;
#[inline]
fn to_owned(&self) -> Self::Owned {
TextString(self.0.to_owned())
}
}
impl ToSocketAddrs for TextStr {
type Iter = vec::IntoIter<SocketAddr>;
#[inline]
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
self.0.to_socket_addrs()
}
}
impl TextError {
#[inline]
pub fn valid_up_to(&self) -> usize {
self.valid_up_to
}
}
impl From<Utf8Error> for TextError {
#[inline]
fn from(err: Utf8Error) -> Self {
Self {
valid_up_to: err.valid_up_to(),
}
}
}
impl Display for TextError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid Basic Text byte sequence from index {}",
self.valid_up_to
)
}
}
impl Error for TextError {}
impl FromTextError {
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[inline]
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
#[inline]
pub fn text_error(&self) -> TextError {
self.error
}
}
impl From<FromUtf8Error> for FromTextError {
#[inline]
fn from(err: FromUtf8Error) -> Self {
let error = err.utf8_error().into();
let bytes = err.into_bytes();
Self { bytes, error }
}
}
impl Display for FromTextError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.error, f)
}
}
impl Error for FromTextError {}
impl From<Box<TextStr>> for Box<[u8]> {
#[inline]
fn from(s: Box<TextStr>) -> Self {
let ptr = Box::into_raw(s);
unsafe { Self::from_raw(ptr as *mut [u8]) }
}
}
impl From<Box<TextStr>> for Box<str> {
#[inline]
fn from(s: Box<TextStr>) -> Self {
let ptr = Box::into_raw(s);
unsafe { Self::from_raw(ptr as *mut str) }
}
}
impl From<Box<TextStr>> for TextString {
#[inline]
fn from(s: Box<TextStr>) -> Self {
s.into_text_string()
}
}
impl From<&'_ Self> for TextString {
#[inline]
fn from(s: &Self) -> Self {
s.clone()
}
}
impl From<&'_ mut TextStr> for TextString {
#[inline]
fn from(s: &mut TextStr) -> Self {
s.to_owned()
}
}
impl From<&'_ TextStr> for TextString {
#[inline]
fn from(s: &TextStr) -> Self {
s.to_owned()
}
}
impl From<Cow<'_, TextStr>> for Box<TextStr> {
#[inline]
fn from(cow: Cow<'_, TextStr>) -> Self {
match cow {
Cow::Borrowed(s) => Self::from(s),
Cow::Owned(s) => Self::from(s),
}
}
}
impl From<TextString> for Box<TextStr> {
#[inline]
fn from(s: TextString) -> Self {
s.into_boxed_text()
}
}
impl Clone for Box<TextStr> {
#[inline]
fn clone(&self) -> Self {
let buf: Box<[u8]> = self.as_bytes().into();
unsafe { TextStr::from_boxed_text_bytes_unchecked(buf) }
}
}
impl Default for Box<TextStr> {
#[inline]
fn default() -> Self {
unsafe { TextStr::from_boxed_text_bytes_unchecked(Box::default()) }
}
}
impl From<&TextStr> for Box<TextStr> {
#[inline]
fn from(s: &TextStr) -> Self {
unsafe { TextStr::from_boxed_text_bytes_unchecked(Box::from(s.as_bytes())) }
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for &'a TextStr {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let size = u.arbitrary_len::<u8>()?;
match TextStr::from_text_bytes(&u.peek_bytes(size).unwrap()) {
Ok(s) => {
u.bytes(size).unwrap();
Ok(s)
}
Err(e) => {
let i = e.valid_up_to();
let valid = u.bytes(i).unwrap();
let s = unsafe {
debug_assert!(TextStr::from_text_bytes(valid).is_ok());
TextStr::from_text_bytes_unchecked(valid)
};
Ok(s)
}
}
}
fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let bytes = u.take_rest();
TextStr::from_text_bytes(bytes)
.map_err(|_| arbitrary::Error::IncorrectFormat)
.map(Into::into)
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and(<usize as arbitrary::Arbitrary>::size_hint(depth), (0, None))
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for TextString {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
<&TextStr as arbitrary::Arbitrary>::arbitrary(u).map(Into::into)
}
fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
<&TextStr as arbitrary::Arbitrary>::arbitrary_take_rest(u).map(Into::into)
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<&TextStr as arbitrary::Arbitrary>::size_hint(depth)
}
}
impl Index<Range<usize>> for TextString {
type Output = TextSubstr;
#[inline]
fn index(&self, index: Range<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
impl Index<Range<usize>> for TextStr {
type Output = TextSubstr;
#[inline]
fn index(&self, index: Range<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
impl Index<RangeTo<usize>> for TextString {
type Output = TextSubstr;
#[inline]
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
impl Index<RangeTo<usize>> for TextStr {
type Output = TextSubstr;
#[inline]
fn index(&self, index: RangeTo<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
impl Index<RangeFrom<usize>> for TextString {
type Output = TextSubstr;
#[inline]
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
impl Index<RangeFrom<usize>> for TextStr {
type Output = TextSubstr;
#[inline]
fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
unsafe { TextSubstr::from_text_unchecked(self.0.index(index)) }
}
}
pub fn default_read_to_text_string<Inner: ReadText + ?Sized>(
inner: &mut Inner,
buf: &mut TextString,
) -> io::Result<usize> {
let start = buf.0.len();
let n = inner.read_to_string(&mut buf.0)?;
if let Some(c) = buf.0[start..].chars().next() {
if !is_basic_text_start(c) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"read_to_text_string requires a starter",
));
}
}
Ok(n)
}
#[test]
fn normalize_string() {
let ring = "\u{30a}";
let unnormal = "A\u{30a}";
let unnormal_nl = "A\u{30a}\n";
let composed = TextStr::from_text("\u{c5}").unwrap();
let composed_nl = TextStr::from_text("\u{c5}\n").unwrap();
assert_eq!(TextStr::from_text(unnormal).unwrap_err().valid_up_to(), 1);
assert_eq!(TextStr::from_text(ring).unwrap_err().valid_up_to(), 0);
assert_eq!(composed, &TextString::from_text_lossy(unnormal));
assert_eq!(composed_nl, &TextString::from_text_lossy(unnormal_nl));
}
#[test]
fn validate_string() {
assert!(TextStr::from_text_bytes(b"").is_ok());
assert_eq!(
TextStr::from_text_bytes(b"\xff").unwrap_err().valid_up_to(),
0
);
}
#[test]
fn split_escape() {
assert_eq!(
TextStr::from_text_bytes(b"\x1b[p")
.unwrap_err()
.valid_up_to(),
0
);
assert_eq!(
TextStr::from_text_bytes(b"\x1b[!")
.unwrap_err()
.valid_up_to(),
0
);
assert_eq!(
TextStr::from_text_bytes(b"\x1b[")
.unwrap_err()
.valid_up_to(),
0
);
assert_eq!(
TextStr::from_text_bytes(b"\x1b").unwrap_err().valid_up_to(),
0
);
}