1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
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()
    }
}