use alloc::borrow::Borrow;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use core::{
fmt::{Debug, Display, Formatter, Write},
num::NonZeroUsize,
};
#[cfg(feature = "std")]
use std::error::Error;
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct Utf32String(Bytes);
#[derive(Clone, PartialEq, Eq, Hash, Default)]
pub struct MysteryString(Bytes);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StringConversionError<T> {
pub num_errors: NonZeroUsize,
pub first_error: usize,
pub lossy: T,
}
impl<T> Display for StringConversionError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if usize::from(self.num_errors) > 1 {
write!(
f,
"string conversion encountered {} unrepresentable characters, the first one at index {}.",
self.num_errors,
self.first_error
)
} else {
write!(
f,
"string conversion encountered an unrepresentable character at index {}.",
self.first_error
)
}
}
}
#[cfg(feature = "std")]
impl<T> Error for StringConversionError<T> where T: Debug {}
impl Utf32String {
pub fn from_chars<I, C>(chars: I) -> Result<Self, StringConversionError<Self>>
where
I: IntoIterator<Item = C>,
C: Borrow<char>,
{
let mut num_errors: usize = 0;
let mut first_error: usize = usize::MAX;
let iter = chars.into_iter();
let mut bm = BytesMut::with_capacity(4 * iter.size_hint().0);
for (i, cref) in iter.enumerate() {
let c = *cref.borrow();
if c == '\0' {
bm.put_u32('\u{2400}'.into());
num_errors += 1;
first_error = first_error.min(i);
} else {
bm.put_u32(c.into())
}
}
if let Some(num_errors) = NonZeroUsize::new(num_errors) {
Err(StringConversionError {
num_errors,
first_error,
lossy: Self(bm.freeze()),
})
} else {
Ok(Self(bm.freeze()))
}
}
pub fn from_chars_lossy<I, C>(chars: I) -> Self
where
I: IntoIterator<Item = C>,
C: Borrow<char>,
{
match Self::from_chars(chars) {
Ok(s) => s,
Err(e) => e.lossy,
}
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn char_len(&self) -> usize {
self.0.len() / 4
}
pub fn byte_len(&self) -> usize {
self.0.len()
}
pub fn byte_len_with_prefix_and_nul(&self) -> usize {
self.0.len() + 8
}
pub fn to_bytes(&self) -> Bytes {
self.clone().into_bytes()
}
pub fn into_bytes(self) -> Bytes {
self.0
}
}
impl Display for Utf32String {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut buf = self.0.clone();
while buf.has_remaining() {
let c: char = buf
.get_u32()
.try_into()
.expect("Utf32String should always contain valid characters");
f.write_char(c)?
}
Ok(())
}
}
impl Debug for Utf32String {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = self.to_string();
f.debug_tuple("Utf32String").field(&s).finish()
}
}
impl TryFrom<String> for Utf32String {
type Error = StringConversionError<Utf32String>;
fn try_from(value: String) -> Result<Self, Self::Error> {
Utf32String::from_chars(value.chars())
}
}
impl TryFrom<&String> for Utf32String {
type Error = StringConversionError<Utf32String>;
fn try_from(value: &String) -> Result<Self, Self::Error> {
Utf32String::from_chars(value.chars())
}
}
impl TryFrom<&str> for Utf32String {
type Error = StringConversionError<Utf32String>;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Utf32String::from_chars(value.chars())
}
}
impl TryFrom<&[char]> for Utf32String {
type Error = StringConversionError<Utf32String>;
fn try_from(value: &[char]) -> Result<Self, Self::Error> {
Utf32String::from_chars(value)
}
}
impl MysteryString {
pub fn from_chars<I, C>(chars: I) -> Result<Self, StringConversionError<Self>>
where
I: IntoIterator<Item = C>,
C: Borrow<char>,
{
let mut num_errors: usize = 0;
let mut first_error: usize = usize::MAX;
let iter = chars.into_iter();
let mut bm = BytesMut::with_capacity(4 * iter.size_hint().0);
for (i, cref) in iter.enumerate() {
match u8::try_from(*cref.borrow()) {
Ok(b) if b != 0 => {
bm.put_u8(b);
}
_ => {
bm.put_u8(b'?');
num_errors += 1;
first_error = first_error.min(i);
}
}
}
if let Some(num_errors) = NonZeroUsize::new(num_errors) {
Err(StringConversionError {
num_errors,
first_error,
lossy: Self(bm.freeze()),
})
} else {
Ok(Self(bm.freeze()))
}
}
pub fn from_bytes<I, C>(chars: I) -> Result<Self, StringConversionError<Self>>
where
I: IntoIterator<Item = C>,
C: Borrow<u8>,
{
let mut failed = false;
let mut num_errors: usize = 0;
let mut first_error: usize = usize::MAX;
let iter = chars.into_iter();
let mut bm = BytesMut::with_capacity(4 * iter.size_hint().0);
for (i, bref) in iter.enumerate() {
let b = *bref.borrow();
if b != 0 {
if !failed {
bm.put_u8(b);
}
} else {
failed = true;
num_errors += 1;
first_error = first_error.min(i);
}
}
if let Some(num_errors) = NonZeroUsize::new(num_errors) {
Err(StringConversionError {
num_errors,
first_error,
lossy: Self(bm.freeze()),
})
} else {
Ok(Self(bm.freeze()))
}
}
pub fn from_chars_lossy<I, C>(chars: I) -> Self
where
I: IntoIterator<Item = C>,
C: Borrow<char>,
{
match Self::from_chars(chars) {
Ok(s) => s,
Err(e) => e.lossy,
}
}
pub fn from_bytes_lossy<I, C>(chars: I) -> Self
where
I: IntoIterator<Item = C>,
C: Borrow<u8>,
{
match Self::from_bytes(chars) {
Ok(s) => s,
Err(e) => e.lossy,
}
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn len_with_prefix_and_nul(&self) -> usize {
self.len() + 2
}
pub fn to_bytes(&self) -> Bytes {
self.clone().into_bytes()
}
pub fn into_bytes(self) -> Bytes {
self.0
}
}
impl Display for MysteryString {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut buf = self.0.clone();
while buf.has_remaining() {
let byte = buf.get_u8();
let c = char::from_u32(byte.into()).unwrap();
f.write_char(c)?
}
Ok(())
}
}
impl Debug for MysteryString {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = self.to_string();
f.debug_tuple("MysteryString").field(&s).finish()
}
}
impl TryFrom<String> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: String) -> Result<Self, Self::Error> {
MysteryString::from_chars(value.chars())
}
}
impl TryFrom<&String> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: &String) -> Result<Self, Self::Error> {
MysteryString::from_chars(value.chars())
}
}
impl TryFrom<&str> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: &str) -> Result<Self, Self::Error> {
MysteryString::from_chars(value.chars())
}
}
impl TryFrom<Vec<u8>> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
MysteryString::from_bytes(value)
}
}
impl TryFrom<&Vec<u8>> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
MysteryString::from_bytes(value)
}
}
impl TryFrom<&[u8]> for MysteryString {
type Error = StringConversionError<MysteryString>;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
MysteryString::from_bytes(value)
}
}