#![deny(future_incompatible)]
#![deny(nonstandard_style)]
#![deny(rust_2018_idioms)]
#![deny(unsafe_code)]
#![warn(missing_docs)]
#![warn(unused)]
use std::{
borrow::Cow,
ops::{Bound, Range, RangeBounds},
};
fn get_start_bound(bound: Bound<&usize>) -> usize {
match bound {
Bound::Included(n) => *n,
Bound::Excluded(n) => *n + 1,
Bound::Unbounded => 0,
}
}
fn get_end_bound(bound: Bound<&usize>, unbounded: usize) -> usize {
match bound {
Bound::Included(n) => *n + 1,
Bound::Excluded(n) => *n,
Bound::Unbounded => unbounded,
}
}
#[derive(Debug)]
struct Splice<'a> {
range: Range<usize>,
value: Cow<'a, str>,
}
#[derive(Debug)]
pub struct Multisplice<'a> {
source: &'a str,
splices: Vec<Splice<'a>>,
}
impl<'a> Multisplice<'a> {
#[inline]
pub fn new(source: &'a str) -> Self {
Multisplice {
source,
splices: vec![],
}
}
#[inline]
pub fn splice(&mut self, start: usize, end: usize, value: impl Into<Cow<'a, str>>) {
self.splice_cow(start, end, value.into())
}
#[inline]
pub fn splice_range(&mut self, range: impl RangeBounds<usize>, value: impl Into<Cow<'a, str>>) {
let start = get_start_bound(range.start_bound());
let end = get_end_bound(range.end_bound(), self.source.len());
self.splice_cow(start, end, value.into())
}
fn splice_cow(&mut self, start: usize, end: usize, value: Cow<'a, str>) {
let mut insert_at = None;
for (i, s) in self.splices.iter().enumerate() {
let range = &s.range;
assert!(
!(range.start <= start && range.end > start),
"Trying to splice an already spliced range"
);
if range.start > start {
insert_at = Some(i);
break;
}
}
let splice = Splice {
range: Range { start, end },
value,
};
match insert_at {
Some(i) => self.splices.insert(i, splice),
None => self.splices.push(splice),
};
}
pub fn slice(&self, start: usize, end: usize) -> Cow<'a, str> {
assert!(end <= self.source.len());
let mut result = String::new();
let mut last = start;
for s in &self.splices {
let range = &s.range;
if range.end <= last {
continue;
}
if range.start >= end {
break;
}
if range.start >= last {
result.push_str(&self.source[last..range.start]);
}
result.push_str(&s.value);
last = range.end;
}
if end >= last {
if result.is_empty() {
return Cow::Borrowed(&self.source[last..end]);
}
result.push_str(&self.source[last..end]);
}
result.into()
}
#[inline]
pub fn slice_range(&self, range: impl RangeBounds<usize>) -> Cow<'a, str> {
let start = get_start_bound(range.start_bound());
let end = get_end_bound(range.end_bound(), self.source.len());
self.slice(start, end)
}
}
impl ToString for Multisplice<'_> {
#[inline]
fn to_string(&self) -> String {
self.slice_range(..).into()
}
}