use std::{fmt, str::Chars};
pub struct StringBuilder<'a> {
original: Chars<'a>,
buffer: String,
#[cfg(debug_assertions)]
remaining: String,
}
impl fmt::Debug for StringBuilder<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_struct = f.debug_struct("StringBuilder");
debug_struct.field("buffer", &self.buffer);
#[cfg(debug_assertions)]
debug_struct.field("remaining", &self.remaining);
debug_struct.finish_non_exhaustive()
}
}
impl StringBuilder<'_> {
pub fn new(original: &str) -> StringBuilder<'_> {
StringBuilder {
original: original.chars(),
buffer: String::with_capacity(original.len()),
#[cfg(debug_assertions)]
remaining: original.to_owned(),
}
}
pub fn insert(&mut self, text: &str) {
self.buffer.push_str(text);
}
pub fn delete(&mut self, length: usize) {
if length == 0 {
return;
}
self.original.nth(length - 1);
#[cfg(debug_assertions)]
{
self.remaining = self.remaining.chars().skip(length).collect();
}
}
pub fn retain(&mut self, length: usize) {
self.buffer.extend(self.original.by_ref().take(length));
#[cfg(debug_assertions)]
{
self.remaining = self.remaining.chars().skip(length).collect();
}
}
pub fn take(&mut self) -> String {
std::mem::take(&mut self.buffer)
}
#[cfg(debug_assertions)]
pub fn get_slice_from_remaining(&self, length: usize) -> String {
let result = self.remaining.chars().take(length).collect::<String>();
debug_assert_eq!(result.chars().count(), length, "Range out of bounds");
result
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_string_builder() {
let original = "aaa bbb ccc";
let mut builder = StringBuilder::new(original);
builder.insert("ddd");
builder.delete(3);
builder.retain(8);
builder.insert(" eee");
assert_eq!(builder.take(), "ddd bbb ccc eee");
let original = "abcde";
let mut builder = StringBuilder::new(original);
builder.retain(1);
builder.delete(3);
builder.retain(1);
assert_eq!(builder.take(), "ae");
}
#[test]
fn test_empty_original() {
let original = "";
let mut builder = StringBuilder::new(original);
builder.insert("test");
assert_eq!(builder.take(), "test");
}
#[test]
fn test_unicode_characters() {
let original = "こんにちは";
let mut builder = StringBuilder::new(original);
builder.retain(3);
builder.insert("世界, "); builder.retain(2);
assert_eq!(builder.take(), "こんに世界, ちは");
}
#[test]
fn test_get_slice() {
let original = "abcdef";
let builder = StringBuilder::new(original);
assert_eq!(builder.get_slice_from_remaining(3), "abc");
let mut builder = StringBuilder::new(original);
builder.retain(2); assert_eq!(builder.get_slice_from_remaining(2), "cd");
}
#[test]
fn test_retain_all() {
let original = "Hello, world!";
let mut builder = StringBuilder::new(original);
builder.retain(original.len());
assert_eq!(builder.take(), original);
}
#[test]
fn test_delete_all() {
let original = "Hello";
let mut builder = StringBuilder::new(original);
builder.delete(original.len());
builder.insert("Hi");
assert_eq!(builder.take(), "Hi");
}
}