#![warn(missing_docs)]
use std::{
borrow::{Borrow, Cow},
ffi::CStr,
fmt,
io::{self, BufRead},
ops::Deref,
};
use encoding_rs::WINDOWS_1252;
use memchr::memchr;
#[repr(transparent)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct Latin1String {
inner: Box<[u8]>,
}
impl Latin1String {
pub const unsafe fn new(inner: Box<[u8]>) -> Self {
Self { inner }
}
pub fn encode(string: &str) -> Cow<Latin1Str> {
let (res, _, _) = WINDOWS_1252.encode(string);
match res {
Cow::Owned(o) => Cow::Owned(Self {
inner: o.into_boxed_slice(),
}),
Cow::Borrowed(b) => Cow::Borrowed(unsafe { Latin1Str::from_bytes_unchecked(b) }),
}
}
pub fn read_cstring<R: BufRead>(reader: &mut R) -> Result<Self, io::Error> {
let mut string: Vec<u8> = Vec::new();
reader.read_until(0x00, &mut string)?;
if string.ends_with(&[0x00]) {
string.pop();
}
Ok(Self {
inner: string.into_boxed_slice(),
})
}
}
impl Borrow<Latin1Str> for Latin1String {
fn borrow(&self) -> &Latin1Str {
unsafe { Latin1Str::from_bytes_unchecked(&self.inner) }
}
}
impl Deref for Latin1String {
type Target = Latin1Str;
fn deref(&self) -> &Self::Target {
self.borrow()
}
}
impl From<Cow<'_, Latin1Str>> for Latin1String {
fn from(cow: Cow<'_, Latin1Str>) -> Self {
cow.into_owned()
}
}
impl From<&Latin1Str> for Latin1String {
fn from(src: &Latin1Str) -> Latin1String {
src.to_owned()
}
}
#[repr(transparent)]
#[derive(PartialEq, PartialOrd, Eq, Ord)]
pub struct Latin1Str {
#[allow(dead_code)]
inner: [u8],
}
impl PartialEq<Latin1String> for Latin1Str {
fn eq(&self, other: &Latin1String) -> bool {
<Latin1Str as PartialEq>::eq(self, other)
}
}
impl PartialEq<Latin1String> for &Latin1Str {
fn eq(&self, other: &Latin1String) -> bool {
<Latin1Str as PartialEq>::eq(*self, other)
}
}
impl PartialEq<Latin1Str> for Latin1String {
fn eq(&self, other: &Latin1Str) -> bool {
<Latin1Str as PartialEq>::eq(self, other)
}
}
impl PartialEq<&Latin1Str> for Latin1String {
fn eq(&self, other: &&Latin1Str) -> bool {
<Latin1Str as PartialEq>::eq(self, *other)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Latin1String {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.deref().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Latin1Str {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.decode().as_ref())
}
}
impl fmt::Debug for &'_ Latin1Str {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.decode().fmt(f)
}
}
impl ToOwned for Latin1Str {
type Owned = Latin1String;
fn to_owned(&self) -> Self::Owned {
Latin1String {
inner: self.as_bytes().into(),
}
}
}
impl Latin1Str {
pub const unsafe fn from_bytes_unchecked(text: &[u8]) -> &Self {
&*(text as *const [u8] as *const Latin1Str)
}
pub fn from_bytes_until_nul(mut bytes: &[u8]) -> &Self {
if let Some(nullpos) = memchr(0, bytes) {
bytes = bytes.split_at(nullpos).0;
}
unsafe { Self::from_bytes_unchecked(bytes) }
}
#[deprecated = "Use `from_bytes_until_nul` instead"]
pub fn new(bytes: &[u8]) -> &Self {
Self::from_bytes_until_nul(bytes)
}
pub const fn as_bytes(&self) -> &[u8] {
&self.inner
}
pub const fn len(&self) -> usize {
self.inner.len()
}
pub const fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn decode(&self) -> Cow<str> {
WINDOWS_1252.decode(self.as_bytes()).0
}
}
impl<'a> From<&'a CStr> for &'a Latin1Str {
fn from(v: &'a CStr) -> Self {
unsafe { Latin1Str::from_bytes_unchecked(v.to_bytes()) }
}
}
#[cfg(test)]
mod tests {
use crate::{Latin1Str, Latin1String};
#[test]
fn test_eq() {
assert_eq!(
Latin1Str::from_bytes_until_nul(b""),
Latin1Str::from_bytes_until_nul(b"\0abc")
);
let slice = Latin1Str::from_bytes_until_nul(b"Hello World!");
let owned = Latin1String::encode("Hello World!").into_owned();
assert!(slice == owned);
assert!(owned == slice);
}
}