pub use errors::FromStrError;
use error::{InvalidUtf8Slice,InvalidUtf8Array};
use Utf8Iterator;
use CharExt;
use U8UtfExt;
extern crate core;
use self::core::{hash, fmt, str};
use self::core::borrow::Borrow;
use self::core::ops::Deref;
use self::core::mem::transmute;
#[cfg(not(feature="no_std"))]
use std::ascii::AsciiExt;
#[cfg(feature="ascii")]
extern crate ascii;
#[cfg(feature="ascii")]
use self::ascii::{AsciiChar,ToAsciiChar,ToAsciiCharError};
#[derive(Default)]
#[derive(PartialEq,Eq, PartialOrd,Ord)]
#[derive(Clone,Copy)]
pub struct Utf8Char {
bytes: [u8; 4],
}
impl str::FromStr for Utf8Char {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, FromStrError> {
if s.is_empty() {
Err(FromStrError::Empty)
} else if s.len() != 1+s.as_bytes()[0].extra_utf8_bytes_unchecked() {
Err(FromStrError::MultipleCodepoints)
} else {
let mut bytes = [0; 4];
bytes[..s.len()].copy_from_slice(s.as_bytes());
Ok(Utf8Char{bytes: bytes})
}
}
}
impl From<char> for Utf8Char {
fn from(c: char) -> Self {
Utf8Char{ bytes: c.to_utf8_array().0 }
}
}
impl From<Utf8Char> for char {
fn from(uc: Utf8Char) -> char {
unsafe{ char::from_utf8_exact_slice_unchecked(&uc.bytes[..uc.len()]) }
}
}
impl IntoIterator for Utf8Char {
type Item=u8;
type IntoIter=Utf8Iterator;
fn into_iter(self) -> Utf8Iterator {
Utf8Iterator::from(self)
}
}
impl AsRef<[u8]> for Utf8Char {
fn as_ref(&self) -> &[u8] {
&self.bytes[..self.len()]
}
}
impl AsRef<str> for Utf8Char {
fn as_ref(&self) -> &str {
unsafe{ str::from_utf8_unchecked( self.as_ref() ) }
}
}
impl Borrow<[u8]> for Utf8Char {
fn borrow(&self) -> &[u8] {
self.as_ref()
}
}
impl Borrow<str> for Utf8Char {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl Deref for Utf8Char {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
#[cfg(not(feature="no_std"))]
impl AsciiExt for Utf8Char {
type Owned = Utf8Char;
fn is_ascii(&self) -> bool {
self.bytes[0].is_ascii()
}
fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
self.to_char().eq_ignore_ascii_case(&other.to_char())
}
fn to_ascii_uppercase(&self) -> Self::Owned {
let ascii = self.bytes[0].to_ascii_uppercase();
if ascii == self.bytes[0] {*self}
else {Utf8Char{ bytes: [ascii,0,0,0] }}
}
fn to_ascii_lowercase(&self) -> Self::Owned {
let ascii = self.bytes[0].to_ascii_lowercase();
if ascii == self.bytes[0] {*self} else {Utf8Char{ bytes: [ascii,0,0,0] }} }
fn make_ascii_uppercase(&mut self) {
*self = self.to_ascii_uppercase()
}
fn make_ascii_lowercase(&mut self) {
*self = self.to_ascii_lowercase();
}
}
#[cfg(feature="ascii")]
impl From<AsciiChar> for Utf8Char {
fn from(ac: AsciiChar) -> Self {
Utf8Char{ bytes: [ac.as_byte(),0,0,0] }
}
}
#[cfg(feature="ascii")]
impl ToAsciiChar for Utf8Char {
fn to_ascii_char(self) -> Result<AsciiChar, ToAsciiCharError> {
self.bytes[0].to_ascii_char()
}
unsafe fn to_ascii_char_unchecked(self) -> AsciiChar {
self.bytes[0].to_ascii_char_unchecked()
}
}
impl hash::Hash for Utf8Char {
fn hash<H : hash::Hasher>(&self, state: &mut H) {
self.to_char().hash(state);
}
}
impl fmt::Debug for Utf8Char {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.to_char(), fmtr)
}
}
impl Utf8Char {
pub fn from_slice_start(src: &[u8]) -> Result<(Self,usize),InvalidUtf8Slice> {
let len = try!(char::from_utf8_slice(src)).1;
let mut bytes = [0; 4];
bytes[..len].copy_from_slice(&src[..len]);
Ok((Utf8Char{bytes: bytes}, len))
}
pub fn from_array(utf8: [u8;4]) -> Result<Self,InvalidUtf8Array> {
try!(char::from_utf8_array(utf8));
let extra = utf8[0].extra_utf8_bytes_unchecked() as u32;
let mask = u32::from_le(0xff_ff_ff_ff >> 8*(3-extra));
let unused_zeroed = mask & unsafe{ transmute::<_,u32>(utf8) };
Ok(unsafe{ transmute(unused_zeroed) })
}
pub fn len(self) -> usize {
self.bytes[0].extra_utf8_bytes_unchecked() + 1
}
pub fn to_char(self) -> char {
self.into()
}
pub fn to_slice(self, dst: &mut[u8]) -> usize {
if self.len() > dst.len() {
panic!("The provided buffer is too small.");
}
dst[..self.len()].copy_from_slice(&self.bytes[..self.len()]);
self.len()
}
pub fn to_array(self) -> ([u8;4],usize) {
(self.bytes, self.len())
}
#[cfg(feature="no_std")]
pub fn is_ascii(&self) -> bool {
self.bytes[0] <= 127
}
}