use std;
use std::char::REPLACEMENT_CHARACTER;
use std::cmp::Ordering;
use std::convert::AsRef;
use std::ffi::OsStr;
use std::fmt::{Debug, Display, Formatter, Write};
use std::mem::transmute;
use std::ops::{Index, IndexMut};
use std::path::Path;
use std::str::{from_utf8, Utf8Error};
mod index;
mod utf8chunks;
pub use self::index::{RawStrIndex, RawStrIndexOutput};
pub use self::utf8chunks::{Utf8Chunk, Utf8ChunksIter};
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawStr {
inner: [u8],
}
impl RawStr {
#[inline]
pub fn from<S: AsRef<RawStr> + ?Sized>(s: &S) -> &Self {
s.as_ref()
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> &Self {
unsafe { transmute::<&[u8], &Self>(bytes) }
}
#[inline]
pub fn from_str(bytes: &str) -> &Self {
Self::from_bytes(bytes.as_bytes())
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.inner
}
#[inline]
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
unsafe { transmute::<&mut [u8], &mut Self>(bytes) }
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.inner
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.inner.as_ptr()
}
#[inline]
pub fn first(&self) -> Option<u8> {
self.inner.first().map(|&x| x)
}
#[inline]
pub fn first_mut(&mut self) -> Option<&mut u8> {
self.inner.first_mut()
}
#[inline]
pub fn last(&self) -> Option<u8> {
self.inner.last().map(|&x| x)
}
#[inline]
pub fn last_mut(&mut self) -> Option<&mut u8> {
self.inner.last_mut()
}
#[inline]
pub fn split_first(&self) -> Option<(u8, &RawStr)> {
self.inner
.split_first()
.map(|(&a, b)| (a, RawStr::from_bytes(b)))
}
#[inline]
pub fn split_first_mut(&mut self) -> Option<(&mut u8, &mut RawStr)> {
self.inner
.split_first_mut()
.map(|(a, b)| (a, RawStr::from_bytes_mut(b)))
}
#[inline]
pub fn split_last(&self) -> Option<(u8, &RawStr)> {
self.inner
.split_last()
.map(|(&a, b)| (a, RawStr::from_bytes(b)))
}
#[inline]
pub fn split_last_mut(&mut self) -> Option<(&mut u8, &mut RawStr)> {
self.inner
.split_last_mut()
.map(|(a, b)| (a, RawStr::from_bytes_mut(b)))
}
#[inline]
pub fn split_at(&self, mid: usize) -> (&RawStr, &RawStr) {
let (a, b) = self.inner.split_at(mid);
(RawStr::from_bytes(a), RawStr::from_bytes(b))
}
#[inline]
pub fn split_at_mut(&mut self, mid: usize) -> (&mut RawStr, &mut RawStr) {
let (a, b) = self.inner.split_at_mut(mid);
(RawStr::from_bytes_mut(a), RawStr::from_bytes_mut(b))
}
#[inline]
pub fn contains_byte(&self, x: u8) -> bool {
self.inner.contains(&x)
}
#[inline]
pub fn starts_with<T: AsRef<RawStr>>(&self, x: T) -> bool {
self.inner.starts_with(x.as_ref().as_bytes())
}
#[inline]
pub fn ends_with<T: AsRef<RawStr>>(&self, x: T) -> bool {
self.inner.ends_with(x.as_ref().as_bytes())
}
#[inline]
pub fn get<I: RawStrIndex>(&self, index: I) -> Option<&I::Output> {
index.get(self)
}
#[inline]
pub fn get_mut<I: RawStrIndex>(&mut self, index: I) -> Option<&mut I::Output> {
index.get_mut(self)
}
#[inline]
pub unsafe fn get_unchecked<I: RawStrIndex>(&self, index: I) -> &I::Output {
index.get_unchecked(self)
}
#[inline]
pub unsafe fn get_unchecked_mut<I: RawStrIndex>(&mut self, index: I) -> &mut I::Output {
index.get_unchecked_mut(self)
}
#[inline]
pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &RawStr {
self.get_unchecked(begin..end)
}
#[inline]
pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut RawStr {
self.get_unchecked_mut(begin..end)
}
#[inline]
pub fn bytes(&self) -> std::iter::Cloned<std::slice::Iter<u8>> {
self.inner.iter().cloned()
}
#[inline]
pub fn bytes_mut(&mut self) -> std::slice::IterMut<u8> {
self.inner.iter_mut()
}
#[inline]
pub fn utf8_chunks(&self) -> Utf8ChunksIter {
Utf8ChunksIter { bytes: &self.inner }
}
#[inline]
pub fn to_str(&self) -> Result<&str, Utf8Error> {
from_utf8(self.as_bytes())
}
#[inline]
pub fn to_osstr(&self) -> Result<&OsStr, Utf8Error> {
self.to_osstr_()
}
#[inline]
pub fn to_path(&self) -> Result<&Path, Utf8Error> {
Ok(Path::new(self.to_osstr()?))
}
#[cfg(unix)]
#[inline]
fn to_osstr_(&self) -> Result<&OsStr, Utf8Error> {
use std::os::unix::ffi::OsStrExt;
Ok(OsStr::from_bytes(self.as_bytes()))
}
#[cfg(not(unix))]
#[inline]
fn to_osstr_(&self) -> Result<&OsStr, Utf8Error> {
Ok(OsStr::new(self.to_str()?))
}
#[inline]
pub fn is_ascii(&self) -> bool {
self.inner.is_ascii()
}
#[inline]
pub fn eq_ignore_ascii_case(&self, other: &RawStr) -> bool {
self.inner.eq_ignore_ascii_case(&other.inner)
}
#[inline]
pub fn make_ascii_uppercase(&mut self) {
self.inner.make_ascii_uppercase()
}
#[inline]
pub fn make_ascii_lowercase(&mut self) {
self.inner.make_ascii_lowercase()
}
}
impl AsRef<RawStr> for RawStr {
#[inline]
fn as_ref(&self) -> &RawStr {
self
}
}
impl AsRef<RawStr> for [u8] {
#[inline]
fn as_ref(&self) -> &RawStr {
RawStr::from_bytes(self)
}
}
impl AsRef<RawStr> for str {
#[inline]
fn as_ref(&self) -> &RawStr {
RawStr::from_bytes(self.as_bytes())
}
}
impl AsRef<[u8]> for RawStr {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl<'a> Default for &'a RawStr {
#[inline]
fn default() -> Self {
RawStr::from_bytes(&[])
}
}
impl<'a> Default for &'a mut RawStr {
#[inline]
fn default() -> Self {
RawStr::from_bytes_mut(&mut [])
}
}
impl<I: RawStrIndex> Index<I> for RawStr {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &I::Output {
index.index(self)
}
}
impl<I: RawStrIndex> IndexMut<I> for RawStr {
#[inline]
fn index_mut(&mut self, index: I) -> &mut I::Output {
index.index_mut(self)
}
}
impl<'a> IntoIterator for &'a RawStr {
type Item = u8;
type IntoIter = std::iter::Cloned<std::slice::Iter<'a, u8>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.bytes()
}
}
impl<'a> IntoIterator for &'a mut RawStr {
type Item = &'a mut u8;
type IntoIter = std::slice::IterMut<'a, u8>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.bytes_mut()
}
}
impl<'a> From<&'a str> for &'a RawStr {
#[inline]
fn from(src: &'a str) -> &'a RawStr {
RawStr::from_str(src)
}
}
impl<'a> From<&'a [u8]> for &'a RawStr {
#[inline]
fn from(src: &'a [u8]) -> &'a RawStr {
RawStr::from_bytes(src)
}
}
impl Display for RawStr {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
for Utf8Chunk { valid, broken } in self.utf8_chunks() {
f.write_str(valid)?;
if !broken.is_empty() {
f.write_char(REPLACEMENT_CHARACTER)?;
}
}
Ok(())
}
}
fn write_escaped_str(f: &mut std::fmt::Formatter, s: &str) -> std::fmt::Result {
let mut written = 0;
for (i, c) in s.char_indices() {
let e = c.escape_debug();
if e.len() != 1 {
f.write_str(&s[written..i])?;
for c in e {
f.write_char(c)?;
}
written = i + c.len_utf8();
}
}
f.write_str(&s[written..])
}
impl Debug for RawStr {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_char('"')?;
for Utf8Chunk { valid, broken } in self.utf8_chunks() {
write_escaped_str(f, valid)?;
for &b in broken {
write!(f, "\\x{:02x}", b)?;
}
}
f.write_char('"')
}
}
macro_rules! impl_ord {
($t:ty) => {
impl PartialEq<$t> for RawStr {
#[inline]
fn eq(&self, other: &$t) -> bool {
<RawStr as PartialEq>::eq(self, other.as_ref())
}
}
impl PartialEq<RawStr> for $t {
#[inline]
fn eq(&self, other: &RawStr) -> bool {
<RawStr as PartialEq>::eq(self.as_ref(), other)
}
}
impl PartialOrd<$t> for RawStr {
#[inline]
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
<RawStr as PartialOrd>::partial_cmp(self, other.as_ref())
}
}
impl PartialOrd<RawStr> for $t {
#[inline]
fn partial_cmp(&self, other: &RawStr) -> Option<Ordering> {
<RawStr as PartialOrd>::partial_cmp(self.as_ref(), other)
}
}
};
}
impl_ord!(str);
impl_ord!([u8]);
impl_ord!(&str);
impl_ord!(&[u8]);
#[test]
fn test_display() {
let a = RawStr::from("1\" μs / °C");
assert_eq!(&format!("{}", a), "1\" μs / °C");
let b = RawStr::from_bytes(b"1 \xFF \xce\xbcs / \xc2\xb0C");
assert_eq!(&format!("{}", b), "1 \u{FFFD} μs / °C");
}
#[test]
fn test_debug() {
let a: &RawStr = RawStr::from("1\" μs / °C");
assert_eq!(&format!("{:?}", a), "\"1\\\" μs / °C\"");
let b: &RawStr = RawStr::from_bytes(b"1 \xFF \xce\xbcs / \xc2\xb0C");
assert_eq!(&format!("{:?}", b), "\"1 \\xff μs / °C\"");
}