#![warn(clippy::all, rust_2018_idioms)]
#![allow(clippy::filter_map, clippy::find_map, clippy::shadow_unrelated, clippy::use_self)]
use std::fmt;
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub trait CaseMapping {
fn canonical_byte(b: u8) -> u8;
}
#[derive(Debug)]
pub struct Ascii;
impl CaseMapping for Ascii {
fn canonical_byte(b: u8) -> u8 {
b.to_ascii_lowercase()
}
}
pub struct Rfc1459Strict;
impl CaseMapping for Rfc1459Strict {
fn canonical_byte(b: u8) -> u8 {
match b {
b'[' => b'{',
b']' => b'}',
b'\\' => b'\\',
b => Ascii::canonical_byte(b),
}
}
}
pub struct Rfc1459;
impl CaseMapping for Rfc1459 {
fn canonical_byte(b: u8) -> u8 {
match b {
b'~' => b'^',
b => Rfc1459Strict::canonical_byte(b),
}
}
}
#[repr(transparent)]
pub struct UniCase<S: ?Sized, C: CaseMapping = Ascii>(PhantomData<C>, S);
impl<S, C> UniCase<S, C>
where C: CaseMapping,
{
pub fn new(s: S) -> Self {
UniCase(PhantomData, s)
}
pub fn into_inner(self) -> S {
self.1
}
}
impl<S, C> UniCase<S, C>
where S: ?Sized,
C: CaseMapping,
{
pub fn get(&self) -> &S {
&self.1
}
}
impl<'a, C> From<&'a str> for &'a UniCase<str, C>
where C: CaseMapping,
{
fn from(s: &'a str) -> &'a UniCase<str, C> {
unsafe { &*(s as *const str as *const UniCase<str, C>) }
}
}
pub fn u(s: &str) -> &UniCase<str> {
s.into()
}
impl<S, C> AsRef<UniCase<str, C>> for UniCase<S, C>
where S: AsRef<str> + ?Sized,
C: CaseMapping,
{
fn as_ref(&self) -> &UniCase<str, C> {
self.1.as_ref().into()
}
}
impl<S, C> Borrow<UniCase<str, C>> for UniCase<S, C>
where S: Borrow<str>,
C: CaseMapping,
{
fn borrow(&self) -> &UniCase<str, C> {
self.1.borrow().into()
}
}
impl<S, C> Hash for UniCase<S, C>
where S: AsRef<str> + ?Sized,
C: CaseMapping,
{
fn hash<H: Hasher>(&self, hasher: &mut H) {
let bytes = self.1.as_ref().as_bytes();
for &byte in bytes {
hasher.write_u8(C::canonical_byte(byte));
}
}
}
impl<S1, S2, C> PartialEq<UniCase<S2, C>> for UniCase<S1, C>
where S1: AsRef<str> + ?Sized,
S2: AsRef<str> + ?Sized,
C: CaseMapping,
{
fn eq(&self, other: &UniCase<S2, C>) -> bool {
let me = self.1.as_ref().as_bytes();
let you = other.1.as_ref().as_bytes();
me.len() == you.len() && me.iter().zip(you).all(|(&a, &b)| {
C::canonical_byte(a) == C::canonical_byte(b)
})
}
}
impl<S, C> Eq for UniCase<S, C>
where S: AsRef<str> + ?Sized,
C: CaseMapping,
{}
impl<S> fmt::Debug for UniCase<S, Ascii>
where S: fmt::Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UniCase<Ascii>({:?})", &self.1)
}
}
impl<S> fmt::Debug for UniCase<S, Rfc1459>
where S: fmt::Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UniCase<Rfc1459>({:?})", &self.1)
}
}
impl<S> fmt::Debug for UniCase<S, Rfc1459Strict>
where S: fmt::Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UniCase<Rfc1459Strict>({:?})", &self.1)
}
}