use std::{
char::DecodeUtf16Error,
fmt::{self, Display},
mem,
};
#[derive(Debug)]
#[repr(transparent)]
pub struct Utf8Str([u8]);
#[derive(Debug)]
#[repr(transparent)]
pub struct Utf16Str([u16]);
#[derive(Debug)]
#[repr(transparent)]
pub struct Utf32Str([u32]);
#[derive(Debug)]
#[repr(transparent)]
pub struct Ucs2Str([u16]);
impl Utf8Str {
pub fn from_str(str: &str) -> &Utf8Str {
const _: () = assert!(mem::size_of::<&[u8]>() == mem::size_of::<&str>());
const _: () = assert!(mem::align_of::<&[u8]>() == mem::align_of::<&str>());
unsafe { Utf8Str::from_utf8_unchecked(str.as_bytes()) }
}
pub fn from_utf8(utf8: &[u8]) -> Result<&Utf8Str, ()> {
let str: &str = std::str::from_utf8(utf8).map_err(|_| ())?;
Ok(Utf8Str::from_str(str))
}
pub fn as_str(&self) -> &str {
let Utf8Str(slice) = self;
unsafe { std::str::from_utf8_unchecked(slice) }
}
pub unsafe fn from_utf8_unchecked(utf8: &[u8]) -> &Utf8Str {
const _: () = assert!(mem::size_of::<&Utf8Str>() == mem::size_of::<&[u8]>());
const _: () = assert!(mem::align_of::<&Utf8Str>() == mem::align_of::<&[u8]>());
std::mem::transmute::<&[u8], &Utf8Str>(utf8)
}
pub fn as_slice(&self) -> &[u8] {
let Utf8Str(slice) = self;
slice
}
}
impl Utf16Str {
pub fn from_utf16(utf16: &[u16]) -> Result<&Utf16Str, DecodeUtf16Error> {
for result in char::decode_utf16(utf16.iter().copied()) {
let _: char = result?;
}
Ok(unsafe { Utf16Str::from_utf16_unchecked(utf16) })
}
pub unsafe fn from_utf16_unchecked(utf16: &[u16]) -> &Utf16Str {
const _: () = assert!(mem::size_of::<&Utf16Str>() == mem::size_of::<&[u16]>());
const _: () = assert!(mem::align_of::<&Utf16Str>() == mem::align_of::<&[u16]>());
std::mem::transmute::<&[u16], &Utf16Str>(utf16)
}
pub fn as_slice(&self) -> &[u16] {
let Utf16Str(slice) = self;
slice
}
}
impl Utf32Str {
pub unsafe fn from_utf32_unchecked(utf32: &[u32]) -> &Utf32Str {
const _: () = assert!(mem::size_of::<&Utf32Str>() == mem::size_of::<&[u32]>());
const _: () = assert!(mem::align_of::<&Utf32Str>() == mem::align_of::<&[u32]>());
std::mem::transmute::<&[u32], &Utf32Str>(utf32)
}
pub fn as_slice(&self) -> &[u32] {
let Utf32Str(slice) = self;
slice
}
}
impl Display for Utf8Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl Display for Utf16Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Utf16Str(slice) = self;
for char in char::decode_utf16(slice.into_iter().copied()) {
let char: char = match char {
Ok(char) => char,
Err(err) => panic!("Utf16Str could not be decoded: {err}"),
};
let () = Display::fmt(&char, f)?;
}
Ok(())
}
}
impl Display for Utf32Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Utf32Str(slice) = self;
for char_u32 in slice.into_iter().copied() {
let char: char = match char::from_u32(char_u32) {
Some(char) => char,
None => panic!("Utf32Str code point is not a valid `char`: {char_u32}"),
};
let () = Display::fmt(&char, f)?;
}
Ok(())
}
}
#[test]
fn test_utf8_str_display() {
assert_eq!(
format!("{}", Utf8Str::from_str("hello 👋")),
String::from("hello 👋")
);
}
#[test]
fn test_utf16_str_display() {
let utf16: Vec<u16> = "hello 👋".encode_utf16().collect();
let utf16: &Utf16Str = Utf16Str::from_utf16(&utf16).unwrap();
assert_eq!(format!("{}", utf16), String::from("hello 👋"));
}
#[test]
fn test_utf32_str_display() {
let utf32: Vec<u32> = "hello 👋"
.chars()
.map(|char: char| u32::from(char))
.collect();
let utf32: &Utf32Str = unsafe { Utf32Str::from_utf32_unchecked(&utf32) };
assert_eq!(format!("{}", utf32), String::from("hello 👋"));
}