mercy 1.1.1

Owned pair of immutable strings.
Documentation
//! [`Stringy`] and [`Stringable`] traits.

#[cfg(any(test, doc, feature = "alloc"))]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, rc::Rc, sync::Arc};
#[cfg(any(test, feature = "alloc"))]
use core::convert::Infallible;
use core::ops::Deref;

#[cfg(feature = "arrayvec")]
use arrayvec::{ArrayString, CapacityError};

/// Generalisation over [`String`].
pub trait Stringy: Default + Deref<Target = str> + AsRef<str> {
    /// Error returned by [`push`](Self::push_back).
    type Error;

    /// Creates an empty string.
    ///
    /// Equivalent to [`default`](Default::default), but doesn't require importing it.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let s = <String as Stringy>::new();
    /// ```
    fn new() -> Self {
        Default::default()
    }

    /// Reserves room for a set number of bytes at the end.
    ///
    /// Equivalent to [`String::reserve`]. Used to make [`push_back_many`] more efficient.
    ///
    /// [`push_back_many`]: Self::push_back_many
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_front("hello");
    /// s.reserve_back(6);
    /// s.push_back("-world");
    /// ```
    fn reserve_back(&mut self, additional: usize);

    /// Reserves room for a set number of bytes at the beginning.
    ///
    /// There isn't an equivalent [`String`] method for this, since it effectively leaves the
    /// string in an invalid state that has to be corrected. However, it's used to make
    /// [`push_front_many`] more efficient.
    ///
    /// [`push_front_many`]: Self::push_front_many
    ///
    /// # Safety
    ///
    /// Valid UTF-8 data must be written to the bytes before accessing the string again, since
    /// the returned bytes are not guaranteed to be valid UTF-8.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_back("world");
    /// unsafe {
    ///     s.reserve_front(6).unwrap().copy_from_slice(b"hello-");
    /// }
    /// assert_eq!(s, "hello-world");
    /// ```
    unsafe fn reserve_front(&mut self, additional: usize) -> Result<&mut [u8], Self::Error>;

    /// Truncates the string to a set length.
    ///
    /// Equivalent to [`String::truncate`], but will also panic if `len` is out-of-bounds.
    ///
    /// # Panics
    ///
    /// Panics if `len` is not a character boundary.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_back("hello-world");
    /// s.truncate_back(5);
    /// assert_eq!(s, "hello");
    /// ```
    fn truncate_back(&mut self, len: usize);

    /// Removes bytes from the beginning of the string.
    ///
    /// Equivalent to <code>[String::drain](..len)</code>.
    ///
    /// # Panics
    ///
    /// Panics if `len` is not a character boundary.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_back("hello-world");
    /// s.remove_front(6);
    /// assert_eq!(s, "world");
    /// ```
    fn remove_front(&mut self, len: usize);

    /// Pushes a string to the back of the buffer.
    ///
    /// Equivalent to [`String::push_str`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_front("hello");
    /// s.push_back("-world");
    /// assert_eq!(s, "hello-world");
    /// ```
    fn push_back(&mut self, s: &str) -> Result<(), Self::Error>;

    /// Pushes a string to the front of the buffer.
    ///
    /// Equivalent to <code>[String::insert](0, s)</code>.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_back("world");
    /// s.push_front("hello-");
    /// assert_eq!(s, "hello-world");
    /// ```
    fn push_front(&mut self, s: &str) -> Result<(), Self::Error>;

    /// Pushes multiple strings to the back of the buffer.
    ///
    /// Has no equivalent [`String`] method.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_front("hello");
    /// s.push_back_many(&["-", "world"]);
    /// assert_eq!(s, "hello-world");
    /// ```
    fn push_back_many(&mut self, s: &[&str]) -> Result<(), Self::Error> {
        self.reserve_back(s.iter().copied().map(str::len).sum());
        s.iter().try_for_each(|s| self.push_back(s))
    }

    /// Pushes multiple strings to the front of the buffer.
    ///
    /// Has no equivalent [`String`] method.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use mercy::Stringy;
    ///
    /// let mut s = String::new();
    /// s.push_front("world");
    /// s.push_front_many(&["hello", "-"]);
    /// assert_eq!(s, "hello-world");
    /// ```
    fn push_front_many(&mut self, s: &[&str]) -> Result<(), Self::Error> {
        // SAFETY: We write the strings to the reserved area.
        unsafe {
            let mut bytes = self.reserve_front(s.iter().copied().map(str::len).sum())?;
            for &s in s.iter() {
                let (lo, hi) = bytes.split_at_mut_unchecked(s.len());
                lo.copy_from_slice(s.as_bytes());
                bytes = hi;
            }
            Ok(())
        }
    }
}
#[cfg(any(test, feature = "alloc"))]
impl Stringy for String {
    type Error = Infallible;
    #[inline]
    fn reserve_back(&mut self, additional: usize) {
        self.reserve(additional);
    }
    #[inline]
    unsafe fn reserve_front(&mut self, additional: usize) -> Result<&mut [u8], Infallible> {
        self.reserve(additional);

        // SAFETY: The caller will put valid UTF-8 data here.
        unsafe {
            let vec = self.as_mut_vec();
            let orig_len = vec.len();
            vec.set_len(vec.len() + additional);
            vec.copy_within(0..orig_len, additional);
            Ok(&mut vec[..additional])
        }
    }
    #[inline]
    fn truncate_back(&mut self, len: usize) {
        assert!(self.is_char_boundary(len));
        self.truncate(len);
    }
    #[inline]
    fn remove_front(&mut self, len: usize) {
        self.drain(..len);
    }
    #[inline]
    fn push_back(&mut self, s: &str) -> Result<(), Infallible> {
        self.push_str(s);
        Ok(())
    }
    #[inline]
    fn push_front(&mut self, s: &str) -> Result<(), Infallible> {
        self.insert_str(0, s);
        Ok(())
    }
}
#[cfg(feature = "arrayvec")]
impl<const N: usize> Stringy for ArrayString<N> {
    type Error = CapacityError;

    #[inline]
    fn reserve_back(&mut self, _: usize) {}

    #[inline]
    unsafe fn reserve_front(&mut self, additional: usize) -> Result<&mut [u8], CapacityError> {
        if additional > self.remaining_capacity() {
            return Err(CapacityError::new(()));
        }

        // SAFETY: The caller will put valid UTF-8 data here.
        unsafe {
            self.set_len(self.len() + additional);
            let bytes = self.as_bytes_mut();
            bytes.copy_within(0.., additional);
            Ok(&mut bytes[..additional])
        }
    }

    #[inline]
    fn truncate_back(&mut self, len: usize) {
        assert!(self.is_char_boundary(len));
        self.truncate(len);
    }

    #[inline]
    fn remove_front(&mut self, len: usize) {
        assert!(self.is_char_boundary(len));

        // SAFETY: We first verify that the position is a character boundary,
        //   and will ensure the string is valid after.
        unsafe {
            self.as_bytes_mut().copy_within(len.., 0);
            self.set_len(self.len() - len);
        }
    }

    #[inline]
    fn push_back(&mut self, s: &str) -> Result<(), CapacityError> {
        self.try_push_str(s).map_err(CapacityError::simplify)
    }
    #[inline]
    fn push_front(&mut self, s: &str) -> Result<(), CapacityError> {
        // SAFETY: We reserve exactly enough space for a valid string.
        unsafe {
            self.reserve_front(s.len())?.copy_from_slice(s.as_bytes());
        }
        Ok(())
    }
}

/// Generalisation over [`String`] that includes immutable and non-growable containers.
///
/// Since types like [`Arc<str>`] and [`Rc<str>`] don't allow mutation at all, and types like
/// [`Box<str>`] trim excess capacity, it can be desirable to build these types initially with
/// [`String`] before converting them into their final form.
///
/// Internally, [`Act`] and [`Spair::new`] use the `Builder` for a string before converting into
/// the final, intended type.
///
/// [`Act`]: crate::Act
/// [`Spair::new`]: crate::Spair::new
pub trait Stringable: From<Self::Builder> + Deref<Target = str> + AsRef<str> {
    /// Builder for this string type.
    type Builder: Stringy;

    /// Makes from a single string.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::sync::Arc;
    /// use mercy::Stringable;
    ///
    /// let Ok(s) = <Arc<str> as Stringable>::from_str("hello");
    /// assert_eq!(&*s, "hello");
    /// ```
    fn from_str(s: &str) -> Result<Self, <Self::Builder as Stringy>::Error> {
        let mut string = Self::Builder::new();
        string.push_back(s)?;
        Ok(string.into())
    }

    /// Makes from multiple strings concatenated together.
    ///
    /// ```rust
    /// use std::sync::Arc;
    /// use mercy::Stringable;
    ///
    /// let Ok(s) = <Arc<str> as Stringable>::from_many(&["hello", "-", "world"]);
    /// assert_eq!(&*s, "hello-world");
    /// ```
    fn from_many(s: &[&str]) -> Result<Self, <Self::Builder as Stringy>::Error> {
        let mut string = Self::Builder::new();
        string.push_back_many(s)?;
        Ok(string.into())
    }
}
impl<T: Stringy> Stringable for T {
    type Builder = T;
}
#[cfg(feature = "alloc")]
impl Stringable for Box<str> {
    type Builder = String;
}
#[cfg(feature = "alloc")]
impl Stringable for Rc<str> {
    type Builder = String;
}
#[cfg(feature = "alloc")]
impl Stringable for Arc<str> {
    type Builder = String;
}