string32 0.2.0

A string that is indexed by u32 instead of usize
Documentation
use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::hash::{Hash, Hasher};

use usize_cast::IntoUsize;

use super::String32;
use super::TryFromStrError;

/// A slice of a `String32`.
///
/// This is just a thin wrapper around [`str`], but with the convenience of an API built around [`u32`] indices instead of [`usize`] indices.
#[derive(Debug, Eq)]
#[repr(transparent)]
pub struct Str32(str);

impl Str32 {
    /// Convert a `&Str32` to a [`&str`] slice.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "123".try_into().unwrap();
    /// assert_eq!("123", s.as_str());
    /// ```
    #[must_use]
    pub const fn as_str(&self) -> &str {
        &self.0
    }

    /// Convert a `&mut Str32` to a `&mut str` slice.
    #[must_use]
    pub fn as_mut_str(&mut self) -> &mut str {
        &mut self.0
    }

    /// Converts the `Str32` to a byte slice.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "123".try_into().unwrap();
    /// assert_eq!(b"123", s.as_bytes());
    /// ```
    #[must_use]
    pub const fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }

    /// Converts the `Str32` to a byte slice.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("123").unwrap();
    /// let bytes = unsafe { s.as_bytes_mut() };
    /// assert_eq!(b"123", bytes);
    /// ```
    ///
    /// # Safety
    ///
    /// See [`str::as_bytes_mut`].
    #[must_use]
    pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
        self.0.as_bytes_mut()
    }

    /// Returns an iterator over the bytes of the string slice.
    #[must_use]
    pub fn bytes(&self) -> std::str::Bytes<'_> {
        self.0.bytes()
    }

    /// Converts the `Str32` to a raw pointer.
    #[must_use]
    pub const fn as_ptr(&self) -> *const u8 {
        self.0.as_ptr()
    }

    /// Converts the `Str32` to a mutable raw pointer.
    ///
    /// The caller must ensure that the string slice is only modified in a way that
    /// ensures it is always valid UTF-8.
    #[must_use]
    pub fn as_mut_ptr(&mut self) -> *mut u8 {
        self.0.as_mut_ptr()
    }

    /// Converts a `&mut str` into a `&mut Str32`.
    ///
    /// # Panics
    ///
    /// Panics if the provided string slice occupies more than [`u32::MAX`] bytes.
    #[must_use]
    pub fn from_mut_str(s: &mut str) -> &mut Self {
        s.try_into().unwrap()
    }

    /// Returns the length of the `Str32` in bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "test".try_into().unwrap();
    /// assert_eq!(4, s.len());
    /// ```
    #[must_use]
    pub fn len(&self) -> u32 {
        self.0.len().try_into().unwrap()
    }

    /// Returns whether the `Str32` is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "".try_into().unwrap();
    /// assert!(s.is_empty());
    /// ```
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over the characters of the `Str32`.
    #[must_use]
    pub fn chars(&self) -> std::str::Chars {
        self.0.chars()
    }

    /// Returns an iterator over the characters of the `Str32`, and their byte indices.
    #[must_use]
    pub fn char_indices(&self) -> impl DoubleEndedIterator<Item = (u32, char)> + '_ {
        self.0
            .char_indices()
            .map(|(i, c)| (i.try_into().unwrap(), c))
    }

    /// Returns an iterator over the lines of a `&Str32`.
    #[must_use]
    pub fn lines(&self) -> impl DoubleEndedIterator<Item = &Self> + '_ {
        self.0.lines().map(|line| line.try_into().unwrap())
    }

    /// Returns an iterator over the ASCII-whitespace-delimited words of a `&Str32`.
    #[must_use]
    pub fn split_ascii_whitespace(&self) -> impl DoubleEndedIterator<Item = &Self> + '_ {
        self.0
            .split_ascii_whitespace()
            .map(|line| line.try_into().unwrap())
    }

    /// Splits a `&Str32` in two at the given byte index.
    ///
    /// # Panics
    ///
    /// Panics if `mid` is not a UTF-8 code point boundary.
    #[must_use]
    pub fn split_at(&self, mid: u32) -> (&Self, &Self) {
        let (s1, s2) = self.0.split_at(mid.into_usize());
        (s1.try_into().unwrap(), s2.try_into().unwrap())
    }

    /// Splits a `&mut Str32` in two at the given byte index.
    ///
    /// # Panics
    ///
    /// Panics if `mid` is not a UTF-8 code point boundary.
    #[must_use]
    pub fn split_at_mut(&mut self, mid: u32) -> (&mut Self, &mut Self) {
        let (s1, s2) = self.0.split_at_mut(mid.into_usize());
        (s1.try_into().unwrap(), s2.try_into().unwrap())
    }

    /// Returns an iterator over the whitespace-delimited words of a `&Str32`.
    #[must_use]
    pub fn split_whitespace(&self) -> impl DoubleEndedIterator<Item = &Self> + '_ {
        self.0
            .split_whitespace()
            .map(|line| line.try_into().unwrap())
    }

    /// Checks if two string slices are equal, ignoring ASCII case mismatches.
    #[must_use]
    pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }

    /// Return an iterator over the string slice's chars, each escaped according to `char::escape_debug`.
    #[must_use]
    pub fn escape_debug(&self) -> std::str::EscapeDebug<'_> {
        self.0.escape_debug()
    }

    /// Return an iterator over the string slice's chars, each escaped according to `char::escape_default`.
    #[must_use]
    pub fn escape_default(&self) -> std::str::EscapeDefault<'_> {
        self.0.escape_default()
    }

    /// Return an iterator over the string slice's chars, each escaped according to `char::escape_unicode`.
    #[must_use]
    pub fn escape_unicode(&self) -> std::str::EscapeUnicode<'_> {
        self.0.escape_unicode()
    }

    /// Returns whether the given index corresponds to a `char` boundary.
    #[must_use]
    pub fn is_char_boundary(&self, index: u32) -> bool {
        self.0.is_char_boundary(index.into_usize())
    }

    /// Converts all uppercase ASCII characters to lowercase.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("ABC").unwrap();
    /// s.make_ascii_lowercase();
    /// assert_eq!("abc", s);
    /// ```
    pub fn make_ascii_lowercase(&mut self) {
        self.0.make_ascii_lowercase()
    }

    /// Converts all lowercase ASCII characters to uppercase.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::String32;
    /// # use std::convert::TryFrom;
    /// let mut s = String32::try_from("abc").unwrap();
    /// s.make_ascii_uppercase();
    /// assert_eq!("ABC", s);
    /// ```
    pub fn make_ascii_uppercase(&mut self) {
        self.0.make_ascii_uppercase()
    }

    /// Parses a `&Str32` slice into another type.
    ///
    /// # Errors
    ///
    /// Will return `Err` if this `&Str32` slice cannot be parsed into the desired type.
    ///
    /// `Err`: `string32::TryFromStringError`
    pub fn parse<F: std::str::FromStr>(&self) -> Result<F, F::Err> {
        self.0.parse()
    }

    /// Create a [`String32`] formed by `n` repetitions of this string slice.
    ///
    /// # Panics
    ///
    /// Panics if the resulting [`String32`] would require more than [`u32::MAX`] bytes.
    #[must_use]
    pub fn repeat(&self, n: u32) -> String32 {
        self.0.repeat(n.into_usize()).try_into().unwrap()
    }

    /// Returns a lowercase equivalent of this `&Str32` as a new [`String32`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "ΑΒΓΔ".try_into().unwrap();
    /// assert_eq!("αβγδ", s.to_lowercase());
    /// ```
    #[must_use]
    pub fn to_lowercase(&self) -> String32 {
        self.0.to_lowercase().try_into().unwrap()
    }

    /// Returns an uppercase equivalent of this `&Str32` as a new [`String32`].
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "αβγδ".try_into().unwrap();
    /// assert_eq!("ΑΒΓΔ", s.to_uppercase());
    /// ```
    #[must_use]
    pub fn to_uppercase(&self) -> String32 {
        self.0.to_uppercase().try_into().unwrap()
    }

    /// Returns a new [`String32`] with each ASCII uppercase character mapped to lowercase.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "TEST".try_into().unwrap();
    /// assert_eq!("test", s.to_ascii_lowercase());
    /// ```
    #[must_use]
    pub fn to_ascii_lowercase(&self) -> String32 {
        self.0.to_ascii_lowercase().try_into().unwrap()
    }

    /// Returns a new [`String32`] with each ASCII lowercase character mapped to uppercase.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = "test".try_into().unwrap();
    /// assert_eq!("TEST", s.to_ascii_uppercase());
    /// ```
    #[must_use]
    pub fn to_ascii_uppercase(&self) -> String32 {
        self.0.to_ascii_uppercase().try_into().unwrap()
    }

    /// Returns a substring of this string with leading and trailing whitespace removed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = " test\t\n ".try_into().unwrap();
    /// assert_eq!("test", s.trim());
    /// ```
    #[must_use]
    pub fn trim(&self) -> &Self {
        self.0.trim().try_into().unwrap()
    }

    /// Returns a substring of this string with leading whitespace removed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = " test\t\n ".try_into().unwrap();
    /// assert_eq!("test\t\n ", s.trim_start());
    /// ```
    #[must_use]
    pub fn trim_start(&self) -> &Self {
        self.0.trim_start().try_into().unwrap()
    }

    /// Returns a substring of this string with trailing whitespace removed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use string32::Str32;
    /// # use std::convert::TryInto;
    /// let s: &Str32 = " test\t\n ".try_into().unwrap();
    /// assert_eq!(" test", s.trim_end());
    /// ```
    #[must_use]
    pub fn trim_end(&self) -> &Self {
        self.0.trim_end().try_into().unwrap()
    }

    /// Convert a `Box<Str32>` into a [`Box<str>`].
    ///
    /// This method has no overhead in the form of copying or allocating.
    #[must_use]
    pub fn into_boxed_str(self: Box<Self>) -> Box<str> {
        self.into()
    }

    /// Convert a `Box<Str32>` into `Box<[u8]>`.
    ///
    /// This method has no overhead in the form of copying or allocating.
    #[must_use]
    pub fn into_boxed_bytes(self: Box<Self>) -> Box<[u8]> {
        self.into()
    }

    /// Convert a `Box<Str32>` into a [`String`].
    ///
    /// This method has no overhead in the form of copying or allocating.
    #[must_use]
    pub fn into_string(self: Box<Self>) -> String {
        self.into()
    }

    /// Convert a `Box<Str32>` into a [`String32`].
    ///
    /// This method has no overhead in the form of copying or allocating.
    #[must_use]
    pub fn into_string32(self: Box<Self>) -> String32 {
        self.into()
    }
}

impl AsRef<[u8]> for Str32 {
    fn as_ref(&self) -> &[u8] {
        self.as_bytes()
    }
}

impl AsRef<str> for Str32 {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Str32 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a> From<&'a Str32> for &'a str {
    fn from(s: &'a Str32) -> Self {
        &s.0
    }
}

impl From<Box<Str32>> for String {
    fn from(b: Box<Str32>) -> Self {
        b.into()
    }
}

impl From<Box<Str32>> for Box<str> {
    fn from(b: Box<Str32>) -> Self {
        b.into()
    }
}

impl From<Box<Str32>> for Box<[u8]> {
    fn from(b: Box<Str32>) -> Self {
        b.into()
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<Box<Str32>> for String32 {
    fn from(b: Box<Str32>) -> Self {
        String::from(b).try_into().unwrap()
    }
}

impl Hash for Str32 {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl Ord for Str32 {
    fn cmp(&self, rhs: &Self) -> cmp::Ordering {
        self.0.cmp(&rhs.0)
    }
}

impl PartialEq for Str32 {
    fn eq(&self, rhs: &Self) -> bool {
        self.0 == rhs.0
    }
}

impl PartialOrd for Str32 {
    fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
        self.0.partial_cmp(&rhs.0)
    }
}

impl ToOwned for Str32 {
    type Owned = String32;

    fn to_owned(&self) -> String32 {
        self.0.to_owned().try_into().unwrap()
    }
}

impl<'a> TryFrom<&'a str> for &'a Str32 {
    type Error = TryFromStrError;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        u32::try_from(s.len())
            .map(|_| {
                let ptr = s as *const str as *const Str32;
                unsafe {
                    // safety: relies on `&Str32` and `&str` having the same layout
                    &*ptr
                }
            })
            .map_err(|_| TryFromStrError(()))
    }
}

impl<'a> TryFrom<&'a mut str> for &'a mut Str32 {
    type Error = TryFromStrError;

    fn try_from(s: &mut str) -> Result<Self, Self::Error> {
        u32::try_from(s.len())
            .map(|_| {
                let ptr = s as *mut str as *mut Str32;
                unsafe {
                    // safety: relies on `&mut Str32` and `&mut str` having the same layout
                    &mut *ptr
                }
            })
            .map_err(|_| TryFromStrError(()))
    }
}