use crate::str_ext::StrExt;
use std::{borrow::Cow, mem::replace};
pub struct CowAppender<'a> {
original: &'a str,
buf: Cow<'a, str>,
last_index: usize,
start_at: usize,
}
impl<'a> CowAppender<'a> {
pub fn new(data: &'a str, start_at: usize) -> Self {
CowAppender {
original: data,
buf: Cow::Borrowed(""),
last_index: start_at,
start_at,
}
}
pub fn append(&mut self, from: usize, to: usize) {
let buf = match replace(&mut self.buf, Cow::Borrowed("")) {
Cow::Owned(mut owned) => {
owned.push_str(&self.original[from..to]);
Cow::Owned(owned)
}
Cow::Borrowed(borrowed) => {
let new = if from == self.last_index {
Cow::Borrowed(&self.original[self.start_at..to])
} else {
let mut s = String::from(borrowed);
s.push_str(&self.original[from..to]);
Cow::Owned(s)
};
self.last_index = to;
new
}
};
self.buf = buf;
}
pub fn push(&mut self, idx: usize) {
self.buf = match replace(&mut self.buf, Cow::Borrowed("")) {
Cow::Owned(mut owned) => {
let c = self
.original
.char_at(idx)
.expect("It's a user's responsibility to provide a correct index");
owned.push(c);
Cow::Owned(owned)
}
Cow::Borrowed(borrowed) => {
if idx == self.last_index {
let len = self
.original
.char_at(idx)
.expect("It's a user's responsibility to provide a correct index")
.len_utf8();
let to = idx + len;
self.last_index = to;
Cow::Borrowed(&self.original[self.start_at..to])
} else {
let mut s = String::from(borrowed);
let c = self
.original
.char_at(idx)
.expect("It's a user's responsibility to provide a correct index");
s.push(c);
Cow::Owned(s)
}
}
};
}
pub fn into_inner(self) -> Cow<'a, str> {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(clippy::ptr_arg)]
fn is_borrowed(s: &Cow<'_, str>) -> bool {
match s {
Cow::Borrowed(_) => true,
Cow::Owned(_) => false,
}
}
#[test]
fn from_zero() {
let mut h = CowAppender::new("lolwut", 0);
h.append(0, 2);
assert!(is_borrowed(&h.buf));
assert_eq!("lo", h.buf);
h.append(2, 4);
assert!(is_borrowed(&h.buf));
assert_eq!("lolw", h.buf);
h.append(5, 6);
assert!(!is_borrowed(&h.buf));
assert_eq!("lolwt", h.buf);
}
#[test]
fn various_len() {
let mut h = CowAppender::new("абв1гдеёжз", 0);
h.append(0, 6);
assert!(is_borrowed(&h.buf));
assert_eq!("абв", h.buf);
h.push(6);
assert!(is_borrowed(&h.buf));
assert_eq!("абв1", h.buf);
h.append(7, 11);
assert!(is_borrowed(&h.buf));
assert_eq!("абв1гд", h.buf);
h.append(13, 19);
assert!(!is_borrowed(&h.buf));
assert_eq!("абв1гдёжз", h.buf);
}
#[test]
fn from_mid() {
let mut h = CowAppender::new("abcdefghij", 3);
h.append(3, 5);
assert!(is_borrowed(&h.buf));
assert_eq!("de", h.buf);
h.append(5, 6);
assert!(is_borrowed(&h.buf));
assert_eq!("def", h.buf);
h.append(7, 9);
assert!(!is_borrowed(&h.buf));
assert_eq!("defhi", h.buf);
}
}