use std::borrow::Cow;
use std::fmt;
use std::str;
#[derive(Debug, Copy, Clone)]
pub enum Lat1Error {
NonAscii,
}
pub struct Lat1Str {
data: [u8],
}
impl Lat1Str {
pub fn from_bytes(bytes: &[u8]) -> &Self {
unsafe { &*(bytes as *const [u8] as *const Self) }
}
pub fn try_from_ascii(str: &str) -> Result<&Self, Lat1Error> {
if str.is_ascii() {
Ok(Self::from_bytes(str.as_bytes()))
} else {
Err(Lat1Error::NonAscii)
}
}
pub fn from_ascii(str: &str) -> &Self {
Self::try_from_ascii(str).unwrap()
}
pub unsafe fn from_ascii_unchecked(str: &str) -> &Self {
Self::from_bytes(str.as_bytes())
}
pub fn from_utf8(str: &str) -> Cow<'_, Lat1Str> {
if str.is_ascii() {
Cow::Borrowed(Lat1Str::from_bytes(str.as_bytes()))
} else {
Cow::Owned(Lat1String::from_utf8(str))
}
}
pub fn is_ascii(&self) -> bool {
self.data.is_ascii()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
pub fn try_as_ascii(&self) -> Result<&str, Lat1Error> {
if self.is_ascii() {
Ok(unsafe { str::from_utf8_unchecked(&self.data) })
} else {
Err(Lat1Error::NonAscii)
}
}
pub fn as_ascii(&self) -> &str {
self.try_as_ascii().unwrap()
}
pub unsafe fn as_ascii_unchecked(&self) -> &str {
str::from_utf8_unchecked(&self.data)
}
pub fn to_utf8(&self) -> Cow<'_, str> {
if self.is_ascii() {
Cow::Borrowed(unsafe { self.as_ascii_unchecked() })
} else {
Cow::Owned(self.data.iter().map(|c| *c as char).collect())
}
}
}
impl std::borrow::ToOwned for Lat1Str {
type Owned = Lat1String;
fn to_owned(&self) -> Self::Owned {
Lat1String {
data: self.as_bytes().to_vec(),
}
}
}
impl fmt::Display for Lat1Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_str(&s)
}
}
impl fmt::Debug for Lat1Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_fmt(format_args!("Lat1(\"{}\")", s))
}
}
#[derive(Clone)]
pub struct Lat1String {
data: Vec<u8>,
}
impl Lat1String {
pub fn from_bytes(bytes: &[u8]) -> Self {
Lat1String {
data: bytes.to_vec(),
}
}
pub fn from_utf8(str: &str) -> Self {
Lat1String {
data: str.chars().map(|c| c as u8).collect(),
}
}
}
impl std::ops::Deref for Lat1String {
type Target = Lat1Str;
fn deref(&self) -> &Self::Target {
Lat1Str::from_bytes(self.data.as_slice())
}
}
impl std::borrow::Borrow<Lat1Str> for Lat1String {
fn borrow(&self) -> &Lat1Str {
Lat1Str::from_bytes(self.data.as_slice())
}
}
impl fmt::Display for Lat1String {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_str(&s)
}
}
impl fmt::Debug for Lat1String {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_fmt(format_args!("Lat1(\"{}\")", s))
}
}
#[derive(Copy, Clone)]
pub struct Lat1StrF<const N: usize> {
data: [u8; N],
}
impl<const N: usize> Lat1StrF<N> {
pub fn from_bytes(bytes: [u8; N]) -> Self {
Self { data: bytes }
}
}
impl<const N: usize> std::ops::Deref for Lat1StrF<N> {
type Target = Lat1Str;
fn deref(&self) -> &Self::Target {
Lat1Str::from_bytes(self.data.as_slice())
}
}
impl<const N: usize> fmt::Display for Lat1StrF<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_str(&s)
}
}
impl<const N: usize> fmt::Debug for Lat1StrF<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = self.to_utf8();
f.write_fmt(format_args!("Lat1(\"{}\")", s))
}
}
#[test]
fn test_latin_str() {
let utf8 = "Mon frère est là.";
let latin1: &[u8] = &[
0x4D, 0x6F, 0x6E, 0x20, 0x66, 0x72, 0xE8, 0x72, 0x65, 0x20, 0x65, 0x73, 0x74, 0x20, 0x6C,
0xE0, 0x2E,
];
let ls = Lat1String::from_utf8(utf8);
assert_eq!(ls.as_bytes(), latin1);
let ls = Lat1Str::from_bytes(latin1);
assert_eq!(ls.to_utf8(), utf8);
}