stackstring 0.1.8

A fixed-size string
Documentation
use super::String;

use crate::error::Error;

/// A builder for the fixed-size [`String`]. Offers pushing of single `byte`s, `char`s,
/// and byte-slices into the buffer.
///
/// ```
/// # use stackstring::{StringBuilder, String, error::Error};
/// let mut builder = StringBuilder::<5>::empty();
///
/// builder.push_char('a');
/// builder.push_char('б');
///
/// assert_eq!(builder.len(), 3);
///
/// assert_eq!(builder.pop(), Some('б'));
///
/// assert_eq!(builder.len(), 1);
///
/// // trying to push beyond the limit is an error
/// let err = builder.push_bytes(b"qwerty");
/// assert_eq!(Err(Error(7)), err);
///
/// assert_eq!(builder.len(), 1);
///
/// builder.push_str("bcde");
///
/// assert_eq!(builder.len(), 5);
///
/// let s = builder.build();
/// assert_eq!(s, "abcde");
/// ```
pub struct StringBuilder<const L: usize> {
    pos: usize,
    res: String<L>,
}

impl<const L: usize> StringBuilder<L> {
    pub const fn empty() -> Self {
        Self {
            pos: 0,
            res: String::empty(),
        }
    }

    pub fn build(self) -> String<L> {
        self.res
    }

    pub fn len(&self) -> usize {
        self.pos
    }

    pub fn pop(&mut self) -> Option<char> {
        let ch = self.res[..self.pos].chars().rev().next()?;
        let newpos = self.len() - ch.len_utf8();
        self.pos = newpos;

        Some(ch)
    }

    pub unsafe fn push_byte_unchecked(&mut self, b: u8) {
        self.res.0[self.pos] = b;
        self.pos += 1;
    }

    pub unsafe fn push_bytes_unchecked(&mut self, s: &[u8]) {
        let lo = self.pos;
        let hi = lo + s.len();

        self.res.0[lo..hi].copy_from_slice(s);
        self.pos = hi;
    }

    pub fn push_byte(&mut self, b: u8) -> Result<(), Error<L>> {
        if self.pos + 1 > L {
            Err((self.pos + 1).into())
        } else {
            Ok(unsafe { self.push_byte_unchecked(b) })
        }
    }

    pub fn push_bytes<S: AsRef<[u8]>>(&mut self, s: S) -> Result<(), Error<L>> {
        let s = s.as_ref();
        if self.pos + s.len() > L {
            Err((self.pos + s.len()).into())
        } else {
            Ok(unsafe { self.push_bytes_unchecked(s) })
        }
    }

    pub fn push_str(&mut self, s: &str) -> Result<(), Error<L>> {
        self.push_bytes(s.as_bytes())
    }

    pub fn push_char(&mut self, ch: char) -> Result<(), Error<L>> {
        match ch.len_utf8() {
            1 => self.push_byte(ch as u8),
            _ => self.push_bytes(ch.encode_utf8(&mut [0; 4]).as_bytes()),
        }
    }
}

impl<const L: usize> Default for StringBuilder<L> {
    fn default() -> Self {
        Self::empty()
    }
}