sedregex 0.2.5

Sed-like regex library
Documentation
use crate::str_ext::StrExt;
use std::{borrow::Cow, mem::replace};

/// A type that helps to append borrowed and owned strings by indices in a uniformed manner.
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);
    }
}