use std::borrow::Cow;
use std::ffi::c_char;
use std::marker::PhantomData;
use std::ops::{Deref, Add, AddAssign};
use self::prims::{EncodingError, BufferUsage};
pub(crate) mod prims;
pub(crate) mod cesu8str;
pub(crate) mod cesu8string;
pub(crate) mod mutf8str;
pub(crate) mod mutf8string;
pub(crate) mod mutf8cstr;
pub(crate) mod mutf8cstring;
pub(crate) mod cross_impls;
pub(crate) mod stream;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum NGCesu8CError {
InteriorNul(usize),
NotNulTerminated,
Encoding(EncodingError),
}
impl From<EncodingError> for NGCesu8CError {
fn from(value: EncodingError) -> Self {
Self::Encoding(value)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct TryFromUtf8Error<'s, S: ?Sized> {
source_str: &'s str,
user_buffer: &'s [u8],
encode_state: BufferUsage,
_phantom: PhantomData<S>,
}
impl<'s, S: ?Sized> TryFromUtf8Error<'s, S> {
pub fn bytes_read(&self) -> usize {
self.encode_state.read
}
pub fn bytes_written(&self) -> usize {
self.encode_state.written
}
pub fn source_str(&self) -> &str {
self.source_str
}
pub fn source_str_used(&self) -> &str {
&self.source_str[..self.encode_state.read]
}
pub fn source_str_rest(&self) -> &str {
&self.source_str[self.encode_state.read..]
}
pub fn encoded_bytes(&self) -> &[u8] {
&self.user_buffer[..self.encode_state.written]
}
}
macro_rules! impl_try_from_utf8_error_finish {
($t: ty, $ownedvariant: literal) => {
impl<'s> TryFromUtf8Error<'s, $t> {
#[doc = concat!("[`", $ownedvariant, "`][prelude::", $ownedvariant,"]")]
pub fn finish(mut self) -> <$t as ToOwned>::Owned {
let mut v = Vec::with_capacity(crate::default_cesu8_capacity(self.source_str.len()) + 1);
v.extend_from_slice(&self.user_buffer[..self.encode_state.written]);
prims::utf8_to_cesu8_io::<{prims::DEFAULT_CHUNK}, {<$t>::ENCODE_NUL}, _>(
self.source_str, false, &mut v, &mut self.encode_state
).unwrap();
if <$t>::NUL_TERM {
v.push(b'\0');
}
unsafe { <$t as ToOwned>::Owned::_from_bytes_unchecked(v) }
}
}
}
}
impl_try_from_utf8_error_finish!(cesu8str::Cesu8Str, "Cesu8String");
impl_try_from_utf8_error_finish!(mutf8str::Mutf8Str, "Mutf8String");
impl_try_from_utf8_error_finish!(mutf8cstr::Mutf8CStr, "Mutf8CString");
macro_rules! impl_from_error_vec {
($e: ident, $errtype: ty, $srcitem: literal) => {
#[doc = concat!("A possible error value when converting a ", $srcitem, " into the requested string.")]
#[doc = "\n\nThis acts as a wrapper type for the actual error, and an owned buffer that can be return"]
#[doc = " the provided allocation."]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct $e {
kind: $errtype,
bytes: Vec<u8>,
}
impl $e {
pub fn kind(&self) -> $errtype {
self.kind
}
pub fn into_inner(self) -> Vec<u8> {
self.bytes
}
}
}
}
impl_from_error_vec!(FromBytesError, EncodingError, "[`Vec<u8>`]");
impl_from_error_vec!(FromBytesWithNulError, NGCesu8CError, "[`Vec<u8>`]");
pub mod prelude {
pub(crate) use crate::ngstr::*;
pub use std::borrow::Cow;
pub use std::ops::Deref;
pub use std::ffi::{CStr, CString};
pub use super::cesu8str::Cesu8Str;
pub use super::cesu8string::Cesu8String;
pub use super::mutf8str::Mutf8Str;
pub use super::mutf8string::Mutf8String;
pub use super::mutf8cstr::Mutf8CStr;
pub use super::mutf8cstring::Mutf8CString;
pub use super::NGCesu8CError;
pub use super::prims::EncodingError;
pub use super::TryFromUtf8Error;
pub use super::FromBytesWithNulError;
}
macro_rules! check_term {
($slice: expr) => {
if let [inner @ .., b'\0'] = $slice { inner } else { panic!("string not nul terminated") }
}
}
pub(crate) use check_term;
macro_rules! impl_str_encoding_meths {
(base, $tyname:tt, $nulterm:tt, $encname:literal) => {
#[doc = concat!("Converts this ", $encname, " string to a byte slice, with its native encoding.")]
#[cfg_attr($nulterm(), doc = "The returned slice will **not** include the trailing nul terminator.")]
#[doc = concat!("# use cesu8str::", stringify!($tyname), ";\n")]
#[cfg_attr(not($nulterm()), doc = concat!(
"let cesu = ", stringify!($tyname), "::try_from_bytes(b\"foo\").unwrap();"
))]
#[cfg_attr($nulterm(), doc = concat!(
"let cesu = ", stringify!($tyname), "::try_from_bytes_with_nul(b\"foo\\0\").unwrap();"
))]
pub fn as_bytes(&self) -> &[u8] {
let b = self._raw_bytes();
if Self::NUL_TERM {
check_term!(b)
} else {
b
}
}
#[doc = concat!("Converts this ", $encname, " string into a UTF-8 string, allocating only when necessary.")]
pub fn to_str(&self) -> Cow<str> {
unsafe { prims::cesu8_to_utf8::<{Self::ENCODE_NUL}>(Cow::Borrowed(self.as_bytes())) }
}
#[doc = concat!("Attempts to convert a UTF-8 string into ", $encname, ". The returned string")]
pub fn try_from_utf8_into_buf<'s>(s: &'s str, buf: &'s mut [u8]) -> Result<&'s Self, TryFromUtf8Error<'s, Self>> {
let valid_up_to = match prims::check_utf8_to_cesu8::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(s.as_bytes()) {
None if ! Self::NUL_TERM => { return Ok(unsafe { Self::_from_bytes_unchecked(s.as_bytes()) });
},
None if Self::NUL_TERM && (buf.len() > s.len()) => { buf[..s.len()].copy_from_slice(s.as_bytes());
buf[s.len()] = b'\0';
return Ok(unsafe { Self::_from_bytes_unchecked(buf) });
},
None => { s.len()
}
Some(err_ind) => { err_ind
},
};
let mut encode_state = BufferUsage::default();
if valid_up_to <= buf.len() {
buf[..valid_up_to].copy_from_slice(&s.as_bytes()[..valid_up_to]);
encode_state.inc(valid_up_to);
}
debug_assert!((encode_state.read < s.len()) || Self::NUL_TERM);
let allocate = if encode_state.written < buf.len() {
let res = {
let cbuf: &mut [u8] = &mut *buf;
let mut c = std::io::Cursor::new(cbuf);
c.set_position(encode_state.written as u64);
let res = prims::utf8_to_cesu8_io::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}, _>(s, true, &mut c, &mut encode_state);
match res {
Ok(_) => debug_assert_eq!(c.position() as usize, encode_state.written, "encoding state write position and cursor position not kept in sync by prims::utf8_to_cesu8_io"),
Err(_) => debug_assert!(c.position() as usize - encode_state.written < 6, "encoding state write position and cursor position not kept in sync by prims::utf8_to_cesu8_io"),
}
res
};
match (Self::NUL_TERM, res) {
(true, Ok(_)) if (encode_state.written < buf.len()) => {
buf[encode_state.written] = b'\0';
encode_state.written += 1;
false
},
(true, Ok(_)) => {
true
}
(false, Ok(_)) => {
false
},
(_, Err(_)) => {
true
}
}
} else {
true
};
if !allocate {
let used = &mut buf[..encode_state.written];
Ok(unsafe { Self::_from_bytes_unchecked(used) })
} else {
Err(TryFromUtf8Error {
source_str: s,
user_buffer: buf,
encode_state,
_phantom: PhantomData,
})
}
}
#[doc = concat!("Converts a UTF-8 string into ", $encname, ". If possible, the string slice will be returned as")]
#[inline]
pub fn from_utf8_into_buf<'s>(s: &'s str, buf: &'s mut [u8]) -> Cow<'s, Self> {
match Self::try_from_utf8_into_buf(s, buf) {
Ok(enc) => Cow::Borrowed(enc),
Err(err) => Cow::Owned(err.finish())
}
}
#[cfg_attr($nulterm(), doc = " The nul-terminator is not included.")]
pub fn len(&self) -> usize {
self.as_bytes().len()
}
#[doc = concat!("use cesu8str::", stringify!($tyname), ";\n")]
#[cfg_attr($nulterm(), doc = concat!("
# fn test() -> Result<(), NGCesu8CError> {
let empty = Mutf8CStr::try_from_bytes_with_nul(b\"\\0\")?;
assert!(empty.is_empty()); // nul-terminator isn't included
let foo = Mutf8CStr::try_from_bytes_with_nul(b\"foo\\0\")?;
assert!(!foo.is_empty());"))]
#[cfg_attr(not($nulterm()), doc = concat!("
# fn test() -> Result<(), NGCesu8CError> {
let empty = ", stringify!($tyname), "::try_from_bytes(b\"\")?;
assert!(empty.is_empty());
let foo = ", stringify!($tyname), "::try_from_bytes(b\"foo\")?;
assert!(!foo.is_empty());"))]
pub fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
#[doc = concat!("Checks that `index`-th byte is the first byte in a ", $encname, " code point")]
#[doc = concat!("# use cesu8str::", stringify!($tyname), "ing;")]
#[doc = concat!("let s = ", stringify!($tyname), "ing::from_utf8(\"Löwe 老虎 Léopard\".to_owned());")]
pub fn is_char_boundary(&self, index: usize) -> bool {
if index == 0 { return true; }
match self.as_bytes().get(index) {
None => index == self.len(),
Some(0xED) => {
let surrogate_pair_one = &self.as_bytes()[index..index+3];
if std::str::from_utf8(surrogate_pair_one).is_ok() {
true
} else {
todo!("is_char_boundary on surrogate pair, test if second codepoint or not");
}
},
Some(&b) => (b as i8) >= -0x40,
}
}
};
(str, $tyname:tt, $nulterm:tt, $encname:literal) => {
#[doc = concat!("Transmutes the byte slice into ", $encname,".")]
#[doc = concat!("The byte slice must be valid ", $encname)]
#[cfg_attr($nulterm(), doc = ", including the nul-terminator")]
#[doc = concat!(". See [", stringify!($tyname), "][cesu8str::", stringify!($tyname), "] for more information regarding their encoding and invariants.")]
pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
if cfg!(debug_assertions) {
if let Err(e) = Self::try_from_bytes(bytes) {
panic!("bad string passed to from_bytes_unchecked: {:?}", e);
}
}
Self::_from_bytes_unchecked(bytes)
}
#[doc = concat!("Validates a byte slice as ", $encname, ". This will never allocate.")]
pub fn try_from_bytes(b: &[u8]) -> Result<&Self, crate::ngstr::prims::EncodingError> {
prims::validate_cesu8::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(b)
.map(|()| unsafe { Self::_from_bytes_unchecked(b) })
}
pub fn try_from_utf8(s: &str) -> Result<&Self, usize> {
match prims::check_utf8_to_cesu8::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(s.as_bytes()) {
None => Ok(unsafe { Self::from_bytes_unchecked(s.as_bytes()) }),
Some(idx) => Err(idx)
}
}
pub fn from_utf8(s: &str) -> Cow<Self> {
match prims::utf8_to_cesu8_vec::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(Cow::Borrowed(s)) {
Cow::Borrowed(b) => Cow::Borrowed(unsafe { Self::_from_bytes_unchecked(b) }),
Cow::Owned(b) => Cow::Owned(unsafe { <Self as ToOwned>::Owned::_from_bytes_unchecked(b) }),
}
}
#[inline]
pub fn encode_utf8_into_writer<W: std::io::Write + fmt::Debug>(s: &str, w: W) -> std::io::Result<usize> {
prims::utf8_to_cesu8_io::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}, W>(s, false, w, &mut BufferUsage::default()).map(|bu| bu.written)
}
};
(cstr, $tyname:tt, $nulterm:tt, $encname:literal) => {
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &Self {
if cfg!(debug_assertions) {
match Self::try_from_bytes_with_nul(bytes) {
Ok(s) => s,
Err(e) => panic!("bad string passed to from_bytes_with_nul_unchecked: {:?}", e)
}
} else {
check_term!(bytes);
Self::_from_bytes_unchecked(bytes)
}
}
pub fn try_from_bytes_with_nul(b: &[u8]) -> Result<&Self, NGCesu8CError> {
let contents: &[u8] = match b {
[rest @ .., b'\0'] => Ok(rest),
[..] => Err(NGCesu8CError::NotNulTerminated),
}?;
let () = prims::validate_cesu8::<{prims::DEFAULT_CHUNK}, true>(contents)?;
Ok(unsafe { Self::_from_bytes_unchecked(b) })
}
#[inline]
pub fn from_utf8_with_nul(src: &str) -> Cow<Self> {
let Some(inner) = src.strip_suffix('\0') else {
panic!("string passed to from_utf8_with_nul not nul terminated: {:?}", src);
};
assert!(!inner.contains('\0'), "string passed to from_utf8_with_nul contains interior nul byte(s): {:?}", src);
match prims::utf8_to_cesu8_vec::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(Cow::Borrowed(inner)) {
Cow::Borrowed(b) => {
debug_assert_eq!(b, inner.as_bytes());
Cow::Borrowed(unsafe { Self::_from_bytes_unchecked(b) })
},
Cow::Owned(mut v) => {
v.reserve_exact(1);
v.push(b'\0');
Cow::Owned(unsafe { <Self as ToOwned>::Owned::_from_bytes_unchecked(v) })
}
}
}
pub fn try_from_utf8_with_nul(s: &str) -> Result<&Self, NGCesu8CError> {
let inner = s.strip_suffix('\0')
.ok_or(NGCesu8CError::NotNulTerminated)?;
match prims::check_utf8_to_cesu8::<{prims::DEFAULT_CHUNK}, {Self::ENCODE_NUL}>(inner.as_bytes()) {
Some(valid_up_to) => {
if let Some(b'\0') = inner.as_bytes().get(valid_up_to+1) {
Err(NGCesu8CError::InteriorNul(valid_up_to+1))
} else {
Err(NGCesu8CError::Encoding(EncodingError {
valid_up_to,
error_len: Some(4.try_into().unwrap()),
}))
}
}
None => {
Ok(unsafe { Self::_from_bytes_unchecked(s.as_bytes()) })
}
}
}
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
let cs = CStr::from_ptr(ptr);
Self::try_from_bytes_with_nul(cs.to_bytes_with_nul()).expect("invalid CStr passed to from_bytes_with_nul")
}
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a Self {
let cs = CStr::from_ptr(ptr);
Self::from_bytes_with_nul_unchecked(cs.to_bytes_with_nul())
}
pub fn as_bytes_with_nul(&self) -> &[u8] {
check_term!(self._raw_bytes());
self._raw_bytes()
}
pub fn as_cstr(&self) -> &CStr {
CStr::from_bytes_with_nul(self.as_bytes_with_nul())
.expect("encoded C-style string does not fit CStr requirements")
}
pub fn len_with_nul(&self) -> usize {
self._raw_bytes().len()
}
pub const fn as_ptr(&self) -> *const c_char {
self.inner.as_ptr() as *const c_char
}
};
}
macro_rules! impl_string_encoding_meths {
(base, $encname:literal) => {
pub fn from_utf8(s: String) -> Self {
let cow = prims::utf8_to_cesu8_vec::<{prims::DEFAULT_CHUNK}, {<Self as Deref>::Target::NUL_TERM}>(std::borrow::Cow::Owned(s));
let mut owned = cow.into_owned();
if <Self as Deref>::Target::NUL_TERM {
owned.reserve_exact(1);
owned.push(b'\0');
}
unsafe { Self::_from_bytes_unchecked(owned) }
}
pub fn into_string(self) -> String {
let mut s = self._into_bytes_unchecked();
if <Self as Deref>::Target::NUL_TERM {
assert_eq!(s.pop(), Some(b'\0'), "last character was not nul terminator in nul terminated string");
}
unsafe {
prims::cesu8_to_utf8::<{<Self as Deref>::Target::ENCODE_NUL}>(Cow::Owned(s))
}.into_owned()
}
pub fn try_from_bytes(b: Vec<u8>) -> Result<Self, FromBytesError> {
if let Err(e) = prims::validate_cesu8::<{prims::DEFAULT_CHUNK}, {<Self as Deref>::Target::ENCODE_NUL}>(&b) {
return Err(FromBytesError { kind: e, bytes: b });
}
let mut b = b;
if <Self as Deref>::Target::NUL_TERM {
b.reserve_exact(1);
b.push(b'\0');
}
Ok(unsafe { Self::_from_bytes_unchecked(b) })
}
pub unsafe fn from_bytes_unchecked(v: Vec<u8>) -> Self {
if cfg!(debug_assertions) {
Self::try_from_bytes(v)
.expect("string passed to from_bytes_unchecked is invalid")
} else {
let mut v = v;
if <Self as Deref>::Target::NUL_TERM {
v.reserve_exact(1);
v.push(b'\0');
}
unsafe { Self::_from_bytes_unchecked(v) }
}
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_bytes(self) -> Vec<u8> {
let mut inner = self._into_bytes_unchecked();
if <Self as Deref>::Target::NUL_TERM {
check_term!(inner.as_slice());
inner.pop().unwrap();
}
inner
}
pub fn with_capacity(mut capacity: usize) -> Self {
if <Self as Deref>::Target::NUL_TERM { capacity += 1; }
let mut v = Vec::with_capacity(capacity);
if <Self as Deref>::Target::NUL_TERM { v.push(b'\0'); }
unsafe { Self::_from_bytes_unchecked(v) }
}
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
pub fn insert_str(&mut self, idx: usize, string: &str) {
assert!(self.is_char_boundary(idx), "provided index is not a valid character boundary");
let nt = <Self as Deref>::Target::NUL_TERM;
if idx == self.as_bytes().len() {
if nt {
check_term!(self.inner.as_slice());
self.inner.pop().unwrap();
}
prims::utf8_to_cesu8_io::<{prims::DEFAULT_CHUNK}, {<Self as Deref>::Target::ENCODE_NUL}, _>(string, false, &mut self.inner, &mut BufferUsage::default()).unwrap();
if nt {
self.inner.push(b'\0');
}
} else {
let encoded = prims::utf8_to_cesu8_vec::<{prims::DEFAULT_CHUNK}, {<Self as Deref>::Target::ENCODE_NUL}>(Cow::Borrowed(string));
self.inner.splice(idx..idx, encoded.iter().copied());
}
}
pub fn insert_at(&mut self, idx: usize, string: &<Self as Deref>::Target) {
assert!(self.is_char_boundary(idx), "provided index is not a valid character boundary");
self.inner.splice(idx..idx, string.as_bytes().iter().copied());
}
};
(string, $encname:literal) => {
};
(cstring, $encname:literal) => {
pub unsafe fn from_bytes_with_nul_unchecked(v: Vec<u8>) -> Self {
if cfg!(debug_assertions) {
<Self as Deref>::Target::try_from_bytes_with_nul(&v).expect("string passed to from_bytes_with_nul_unchecked is invalid");
}
unsafe { Self::_from_bytes_unchecked(v) }
}
pub fn try_from_bytes_with_nul(v: Vec<u8>) -> Result<Self, FromBytesWithNulError> {
match <Self as Deref>::Target::try_from_bytes_with_nul(&v) {
Ok(_) => Ok(
unsafe { Self::_from_bytes_unchecked(v) }
),
Err(err) => Err(FromBytesWithNulError {
kind: err,
bytes: v,
}),
}
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_bytes_with_nul(self) -> Vec<u8> {
self._into_bytes_unchecked()
}
pub fn from_utf8_with_nul(mut s: String) -> Self {
let Some('\0') = s.pop() else {
panic!("string passed to from_utf8_with_nul did not contain a nul terminator!");
};
let raw_cow = prims::utf8_to_cesu8_vec::<{prims::DEFAULT_CHUNK}, true>(Cow::Owned(s));
let mut raw = raw_cow.into_owned();
raw.push(b'\0');
unsafe { Self::from_bytes_with_nul_unchecked(raw) }
}
pub fn into_cstring(self) -> CString {
unsafe { CString::from_vec_with_nul_unchecked(self._into_bytes_unchecked()) }
}
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_raw(self) -> *mut c_char {
let boxed = self._into_bytes_unchecked().into_boxed_slice();
Box::into_raw(boxed) as *mut c_char
}
#[must_use = "call `drop(from_raw(ptr))` if you intend to drop the `Mutf8CString`"]
pub unsafe fn from_raw(ptr: *mut c_char) -> Self {
unsafe {
extern "C" {
fn strlen(s: *const c_char) -> usize;
}
let len = strlen(ptr) + 1; let slice = std::slice::from_raw_parts_mut(ptr, len);
Self::_from_bytes_unchecked(Box::from_raw(slice as *mut [c_char] as *mut [u8]).into_vec())
}
}
};
}
macro_rules! impl_simple_str_traits {
(base $S:ty, $encname:literal) => {
impl fmt::Debug for $S {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<str as fmt::Debug>::fmt(&self.to_str(), f)
}
}
impl fmt::Display for $S {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<str as fmt::Display>::fmt(&self.to_str(), f)
}
}
impl Hash for $S {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.to_str().hash(state);
}
}
};
(str $S:ty, $encname:literal) => {
impl<'a> Add<&'_ str> for &$S {
type Output = <$S as ToOwned>::Owned;
fn add(self, rhs: &str) -> Self::Output {
self.to_owned() + rhs
}
}
impl<'a> Add<&'_ $S> for &$S {
type Output = <$S as ToOwned>::Owned;
fn add(self, rhs: &$S) -> Self::Output {
self.to_owned() + rhs
}
}
impl<'a> Add<&'_ $S> for String {
type Output = String;
fn add(mut self, rhs: &$S) -> Self {
self.push_str(&rhs.to_str());
self
}
}
impl<'a> Add<&'_ $S> for Cow<'a, $S> {
type Output = Self;
fn add(self, rhs: &$S) -> Self {
if rhs.is_empty() { return self; }
Cow::Owned(self.into_owned() + rhs)
}
}
};
(string $S:ty, $encname:literal) => {
impl From<String> for $S {
fn from(s: String) -> $S {
<$S>::from_utf8(s)
}
}
impl From<&'_ str> for $S {
fn from(s: &str) -> $S {
<$S>::from_utf8(s.to_string())
}
}
impl From<$S> for String {
fn from(s: $S) -> Self {
s.into_string()
}
}
impl Add<&'_ str> for $S {
type Output = $S;
fn add(mut self, rhs: &str) -> Self::Output {
self += rhs;
self
}
}
impl Add<&'_ <$S as Deref>::Target> for $S {
type Output = $S;
fn add(mut self, rhs: &<$S as Deref>::Target) -> Self::Output {
self += rhs;
self
}
}
impl AddAssign<&'_ str> for $S {
fn add_assign(&mut self, rhs: &str) {
self.insert_str(self.len(), rhs)
}
}
impl AddAssign<&'_ <$S as Deref>::Target> for $S {
fn add_assign(&mut self, rhs: &'_ <$S as Deref>::Target) {
self.insert_at(self.len(), rhs)
}
}
impl fmt::Write for $S {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
*self += s;
Ok(())
}
}
}
}
use std::fmt;
use std::hash::Hash;
impl_simple_str_traits!(base cesu8str::Cesu8Str, "CESU-8");
impl_simple_str_traits!(base mutf8str::Mutf8Str, "MUTF-8");
impl_simple_str_traits!(base mutf8cstr::Mutf8CStr, "MUTF-8");
impl_simple_str_traits!(str cesu8str::Cesu8Str, "CESU-8");
impl_simple_str_traits!(str mutf8str::Mutf8Str, "MUTF-8");
impl_simple_str_traits!(str mutf8cstr::Mutf8CStr, "MUTF-8");
impl_simple_str_traits!(base cesu8string::Cesu8String, "CESU-8");
impl_simple_str_traits!(base mutf8string::Mutf8String, "MUTF-8");
impl_simple_str_traits!(base mutf8cstring::Mutf8CString, "MUTF-8");
impl_simple_str_traits!(string cesu8string::Cesu8String, "CESU-8");
impl_simple_str_traits!(string mutf8string::Mutf8String, "MUTF-8");
impl_simple_str_traits!(string mutf8cstring::Mutf8CString, "MUTF-8");
impl<'b> TryFrom<&'b [u8]> for &'b cesu8str::Cesu8Str {
type Error = EncodingError;
fn try_from(value: &'b [u8]) -> Result<Self, Self::Error> {
cesu8str::Cesu8Str::try_from_bytes(value)
}
}
impl<'b> TryFrom<&'b [u8]> for &'b mutf8str::Mutf8Str {
type Error = EncodingError;
fn try_from(value: &'b [u8]) -> Result<Self, Self::Error> {
mutf8str::Mutf8Str::try_from_bytes(value)
}
}
impl TryFrom<Vec<u8>> for cesu8string::Cesu8String {
type Error = FromBytesError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
cesu8string::Cesu8String::try_from_bytes(value)
}
}
impl TryFrom<Vec<u8>> for mutf8string::Mutf8String {
type Error = FromBytesError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
mutf8string::Mutf8String::try_from_bytes(value)
}
}
#[test]
fn strings_impl_expected_traits() {
use crate::prelude::*;
trait ExpectedTraitsBorrowed<'s, SB:
?Sized
+ fmt::Debug + fmt::Display + std::hash::Hash + ToOwned
> where
for<'b> &'b SB: Add<&'b str, Output = <SB as ToOwned>::Owned>,
{}
trait ExpectedTraitsOwned<SO:
Sized
+ fmt::Debug + fmt::Display + std::hash::Hash + Deref + Clone
+ From<String> + Into<String>
> where
for<'s> SO: Add<&'s str, Output = SO>,
for<'s> SO: Add<&'s <SO as Deref>::Target, Output = SO>,
for<'s> SO: AddAssign<&'s str> + AddAssign<&'s <SO as Deref>::Target>,
for<'s> SO: From<&'s str> + From<String>,
{}
impl ExpectedTraitsBorrowed<'_, Cesu8Str> for () {}
impl ExpectedTraitsBorrowed<'_, Mutf8Str> for () {}
impl ExpectedTraitsBorrowed<'_, Mutf8CStr> for () {}
impl ExpectedTraitsOwned<Cesu8String> for () {}
impl ExpectedTraitsOwned<Mutf8String> for () {}
impl ExpectedTraitsOwned<Mutf8CString> for () {}
}
pub(crate) use {impl_str_encoding_meths, impl_string_encoding_meths, impl_simple_str_traits};