use core::{borrow, cmp, fmt, hash, ops, str};
use core::convert::Infallible;
use crate::builder::{
BuilderAppendError, EmptyBuilder, FreezeBuilder, FromBuilder,
OctetsBuilder, Truncate, infallible
};
use crate::octets::OctetsFrom;
#[derive(Clone, Default)]
pub struct Str<Octets: ?Sized>(Octets);
impl<Octets> Str<Octets> {
pub fn from_utf8(octets: Octets) -> Result<Self, FromUtf8Error<Octets>>
where Octets: AsRef<[u8]> {
if let Err(error) = str::from_utf8(octets.as_ref()) {
Err(FromUtf8Error { octets, error })
}
else {
Ok(Self(octets))
}
}
pub unsafe fn from_utf8_unchecked(octets: Octets) -> Self {
Self(octets)
}
pub fn try_copy_from_str(
s: &str
) -> Result<Self, BuilderAppendError<Octets>>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
{
let mut res = <Octets as FromBuilder>::Builder::with_capacity(s.len());
res.append_slice(s.as_bytes())?;
Ok(unsafe { Self::from_utf8_unchecked(res.freeze()) })
}
pub fn copy_from_str(s: &str) -> Self
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
<<Octets as FromBuilder>::Builder as OctetsBuilder>::AppendError:
Into<Infallible>,
{
infallible(Self::try_copy_from_str(s))
}
}
impl Str<[u8]> {
pub fn from_utf8_slice(
slice: &[u8]
) -> Result<&Self, FromUtf8Error<&[u8]>> {
match str::from_utf8(slice) {
Ok(s) => Ok(Self::from_str(s)),
Err(error) => Err(FromUtf8Error { octets: slice, error })
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> &Self {
unsafe { &*(s as *const str as *const Self) }
}
}
#[cfg(feature = "std")]
impl Str<std::vec::Vec<u8>> {
pub fn from_string(s: std::string::String) -> Self {
unsafe { Self::from_utf8_unchecked(s.into_bytes()) }
}
}
impl<Octets> Str<Octets> {
pub fn into_octets(self) -> Octets {
self.0
}
}
impl<Octets: ?Sized> Str<Octets> {
pub fn as_str(&self) -> &str
where Octets: AsRef<[u8]> {
unsafe { str::from_utf8_unchecked(self.0.as_ref()) }
}
pub fn as_str_mut(&mut self) -> &mut str
where Octets: AsMut<[u8]> {
unsafe { str::from_utf8_unchecked_mut(self.0.as_mut()) }
}
pub fn as_octets(&self) -> &Octets {
&self.0
}
pub unsafe fn as_octets_mut(&mut self) -> &mut Octets {
&mut self.0
}
pub fn as_slice(&self) -> &[u8]
where Octets: AsRef<[u8]> {
self.0.as_ref()
}
pub unsafe fn as_slice_mut(&mut self) -> &mut [u8]
where Octets: AsMut<[u8]> {
self.0.as_mut()
}
pub fn len(&self) -> usize
where Octets: AsRef<[u8]> {
self.0.as_ref().len()
}
pub fn is_empty(&self) -> bool
where Octets: AsRef<[u8]> {
self.0.as_ref().is_empty()
}
}
impl<Octs, SrcOcts> OctetsFrom<Str<SrcOcts>> for Str<Octs>
where
Octs: OctetsFrom<SrcOcts>
{
type Error = Octs::Error;
fn try_octets_from(src: Str<SrcOcts>) -> Result<Self, Self::Error> {
Octs::try_octets_from(src.into_octets()).map(|octs| unsafe {
Self::from_utf8_unchecked(octs)
})
}
}
impl<Octets: AsRef<[u8]> + ?Sized> ops::Deref for Str<Octets> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<Octets> ops::DerefMut for Str<Octets>
where Octets: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> AsRef<str> for Str<Octets>{
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> AsRef<[u8]> for Str<Octets>{
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octets: AsMut<[u8]> + ?Sized> AsMut<str> for Str<Octets> {
fn as_mut(&mut self) -> &mut str {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> borrow::Borrow<str> for Str<Octets>{
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<Octets> borrow::BorrowMut<str> for Str<Octets>
where Octets: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
fn borrow_mut(&mut self) -> &mut str {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Debug for Str<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Display for Str<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl<Octets, Other> PartialEq<Other> for Str<Octets>
where
Octets: AsRef<[u8]> + ?Sized,
Other: AsRef<str> + ?Sized,
{
fn eq(&self, other: &Other) -> bool {
self.as_str().eq(other.as_ref())
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Eq for Str<Octets> { }
impl<Octets: AsRef<[u8]> + ?Sized> hash::Hash for Str<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl<Octets, Other> PartialOrd<Other> for Str<Octets>
where
Octets: AsRef<[u8]> + ?Sized,
Other: AsRef<str> + ?Sized,
{
fn partial_cmp(&self, other: &Other) -> Option<cmp::Ordering> {
self.as_str().partial_cmp(other.as_ref())
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Ord for Str<Octets> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
pub struct StrBuilder<Octets>(Octets);
impl<Octets> StrBuilder<Octets> {
pub fn new() -> Self
where Octets: EmptyBuilder {
StrBuilder(Octets::empty())
}
pub fn with_capacity(capacity: usize) -> Self
where Octets: EmptyBuilder {
StrBuilder(Octets::with_capacity(capacity))
}
pub fn from_utf8(octets: Octets) -> Result<Self, FromUtf8Error<Octets>>
where Octets: AsRef<[u8]> {
if let Err(error) = str::from_utf8(octets.as_ref()) {
Err(FromUtf8Error { octets, error })
}
else {
Ok(Self(octets))
}
}
pub fn try_from_utf8_lossy(
octets: Octets
) -> Result<Self, Octets::AppendError>
where Octets: AsRef<[u8]> + OctetsBuilder + EmptyBuilder {
const REPLACEMENT_CHAR: &[u8] = &[239, 191, 189];
let mut err = match str::from_utf8(octets.as_ref()) {
Ok(_) => return Ok(Self(octets)),
Err(err) => err,
};
let mut octets = octets.as_ref();
let mut res = Octets::with_capacity(octets.len());
while !octets.is_empty() {
if err.valid_up_to() > 0 {
res.append_slice(&octets[..err.valid_up_to()])?;
}
res.append_slice(REPLACEMENT_CHAR)?;
octets = match err.error_len() {
Some(len) => &octets[err.valid_up_to() + len ..],
None => b""
};
err = match str::from_utf8(octets) {
Ok(_) => {
res.append_slice(octets)?;
break;
}
Err(err) => err,
};
}
Ok(Self(res))
}
pub fn from_utf8_lossy(octets: Octets) -> Self
where
Octets: AsRef<[u8]> + OctetsBuilder + EmptyBuilder,
Octets::AppendError: Into<Infallible>
{
infallible(Self::try_from_utf8_lossy(octets))
}
pub unsafe fn from_utf8_unchecked(octets: Octets) -> Self {
Self(octets)
}
pub fn into_octets_builder(self) -> Octets {
self.0
}
pub fn freeze(self) -> Str<Octets::Octets>
where Octets: FreezeBuilder {
Str(self.0.freeze())
}
pub fn as_str(&self) -> &str
where Octets: AsRef<[u8]> {
unsafe { str::from_utf8_unchecked(self.0.as_ref()) }
}
pub fn as_str_mut(&mut self) -> &mut str
where Octets: AsMut<[u8]> {
unsafe { str::from_utf8_unchecked_mut(self.0.as_mut()) }
}
pub fn as_slice(&self) -> &[u8]
where Octets: AsRef<[u8]> {
self.0.as_ref()
}
pub fn len(&self) -> usize
where Octets: AsRef<[u8]> {
self.0.as_ref().len()
}
pub fn is_empty(&self) -> bool
where Octets: AsRef<[u8]> {
self.0.as_ref().is_empty()
}
pub fn try_push_str(
&mut self, s: &str,
) -> Result<(), Octets::AppendError>
where Octets: OctetsBuilder {
self.0.append_slice(s.as_bytes())
}
pub fn push_str(
&mut self, s: &str,
)
where Octets: OctetsBuilder, Octets::AppendError: Into<Infallible> {
infallible(self.try_push_str(s))
}
pub fn try_push(
&mut self, ch: char
) -> Result<(), Octets::AppendError>
where Octets: OctetsBuilder {
let mut buf = [0u8; 4];
self.0.append_slice(ch.encode_utf8(&mut buf).as_bytes())
}
pub fn push(&mut self, ch: char)
where Octets: OctetsBuilder, Octets::AppendError: Into<Infallible> {
infallible(self.try_push(ch))
}
pub fn truncate(&mut self, new_len: usize)
where Octets: AsRef<[u8]> + Truncate {
if new_len < self.len() {
assert!(self.as_str().is_char_boundary(new_len));
self.0.truncate(new_len)
}
}
pub fn clear(&mut self)
where Octets: AsRef<[u8]> + Truncate {
self.truncate(0)
}
pub fn pop(&mut self) -> Option<char>
where Octets: AsRef<[u8]> + Truncate {
let ch = self.as_str().chars().next_back()?;
self.truncate(self.len() - ch.len_utf8());
Some(ch)
}
}
impl<Octets: EmptyBuilder> Default for StrBuilder<Octets> {
fn default() -> Self {
Self::new()
}
}
impl<Octets: AsRef<[u8]>> ops::Deref for StrBuilder<Octets> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl<Octets: AsRef<[u8]> + AsMut<[u8]>> ops::DerefMut for StrBuilder<Octets> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]>> AsRef<str> for StrBuilder<Octets>{
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<Octets: AsRef<[u8]>> AsRef<[u8]> for StrBuilder<Octets>{
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octets: AsMut<[u8]>> AsMut<str> for StrBuilder<Octets> {
fn as_mut(&mut self) -> &mut str {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]>> borrow::Borrow<str> for StrBuilder<Octets>{
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<Octets> borrow::BorrowMut<str> for StrBuilder<Octets>
where Octets: AsRef<[u8]> + AsMut<[u8]> {
fn borrow_mut(&mut self) -> &mut str {
self.as_str_mut()
}
}
impl<Octets: AsRef<[u8]>> fmt::Debug for StrBuilder<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl<Octets: AsRef<[u8]>> fmt::Display for StrBuilder<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl<Octets, Other> PartialEq<Other> for StrBuilder<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<str>,
{
fn eq(&self, other: &Other) -> bool {
self.as_str().eq(other.as_ref())
}
}
impl<Octets: AsRef<[u8]>> Eq for StrBuilder<Octets> { }
impl<Octets: AsRef<[u8]>> hash::Hash for StrBuilder<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl<Octets, Other> PartialOrd<Other> for StrBuilder<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<str>,
{
fn partial_cmp(&self, other: &Other) -> Option<cmp::Ordering> {
self.as_str().partial_cmp(other.as_ref())
}
}
impl<Octets: AsRef<[u8]>> Ord for StrBuilder<Octets> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct FromUtf8Error<Octets> {
octets: Octets,
error: str::Utf8Error,
}
impl<Octets> FromUtf8Error<Octets> {
pub fn as_slice(&self) -> &[u8]
where Octets: AsRef<[u8]> {
self.octets.as_ref()
}
pub fn into_octets(self) -> Octets {
self.octets
}
pub fn utf8_error(&self) -> str::Utf8Error {
self.error
}
}
impl<Octets> fmt::Debug for FromUtf8Error<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("FromUtf8Error")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl<Octets> fmt::Display for FromUtf8Error<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.error, f)
}
}
#[cfg(feature = "std")]
impl<Octets> std::error::Error for FromUtf8Error<Octets> {}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(feature = "std")]
fn from_utf8_lossy() {
fn check(src: impl AsRef<[u8]>) {
assert_eq!(
StrBuilder::from_utf8_lossy(std::vec::Vec::from(src.as_ref())),
std::string::String::from_utf8_lossy(src.as_ref())
);
}
check(b"hello");
check("ศไทย中华Việt Nam");
check(b"Hello\xC2 There\xFF Goodbye");
check(b"Hello\xC0\x80 There\xE6\x83 Goodbye");
check(b"\xF5foo\xF5\x80bar");
check(b"\xF1foo\xF1\x80bar\xF1\x80\x80baz");
check(b"\xF4foo\xF4\x80bar\xF4\xBFbaz");
check(b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar");
check(b"\xED\xA0\x80foo\xED\xBF\xBFbar");
}
#[test]
#[cfg(feature = "std")]
fn push_str() {
let mut s = StrBuilder::<std::vec::Vec<u8>>::new();
s.push_str("");
assert_eq!(&s[0..], "");
s.push_str("abc");
assert_eq!(&s[0..], "abc");
s.push_str("ประเทศไทย中华Việt Nam");
assert_eq!(&s[0..], "abcประเทศไทย中华Việt Nam");
}
#[test]
#[cfg(feature = "std")]
fn push() {
let mut data = StrBuilder::from_utf8(
std::vec::Vec::from("ประเทศไทย中".as_bytes())
).unwrap();
data.push('华');
data.push('b'); data.push('¢'); data.push('€'); data.push('𤭢'); assert_eq!(data, "ประเทศไทย中华b¢€𤭢");
}
#[test]
#[cfg(feature = "std")]
fn pop() {
let mut data = StrBuilder::from_utf8(
std::vec::Vec::from("ประเทศไทย中华b¢€𤭢".as_bytes())
).unwrap();
assert_eq!(data.pop().unwrap(), '𤭢'); assert_eq!(data.pop().unwrap(), '€'); assert_eq!(data.pop().unwrap(), '¢'); assert_eq!(data.pop().unwrap(), 'b'); assert_eq!(data.pop().unwrap(), '华');
assert_eq!(data, "ประเทศไทย中");
}
}