#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PieceSource {
Original,
Added,
}
#[derive(Debug, Clone)]
pub struct Piece {
pub source: PieceSource,
pub start: usize,
pub length: usize,
}
pub struct PieceTable {
original: Vec<char>,
added: Vec<char>,
pieces: Vec<Piece>,
}
impl PieceTable {
pub fn new(initial: &str) -> Self {
let original: Vec<char> = initial.chars().collect();
let len = original.len();
let pieces = if len > 0 {
vec![Piece {
source: PieceSource::Original,
start: 0,
length: len,
}]
} else {
Vec::new()
};
Self {
original,
added: Vec::new(),
pieces,
}
}
pub fn len(&self) -> usize {
self.pieces.iter().map(|p| p.length).sum()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn insert(&mut self, pos: usize, text: &str) {
let add_start = self.added.len();
self.added.extend(text.chars());
let add_len = self.added.len() - add_start;
if add_len == 0 {
return;
}
let new_piece = Piece {
source: PieceSource::Added,
start: add_start,
length: add_len,
};
let mut cur = 0usize;
let mut insert_idx = self.pieces.len();
for (i, p) in self.pieces.iter().enumerate() {
if cur + p.length >= pos {
let offset = pos - cur;
if offset == 0 {
insert_idx = i;
} else if offset == p.length {
insert_idx = i + 1;
} else {
let left = Piece {
source: p.source,
start: p.start,
length: offset,
};
let right = Piece {
source: p.source,
start: p.start + offset,
length: p.length - offset,
};
self.pieces.remove(i);
self.pieces.insert(i, right);
self.pieces.insert(i, new_piece.clone());
self.pieces.insert(i, left);
return;
}
break;
}
cur += p.length;
}
self.pieces.insert(insert_idx, new_piece);
}
pub fn as_string(&self) -> String {
let mut s = String::with_capacity(self.len());
for p in &self.pieces {
let src = match p.source {
PieceSource::Original => &self.original,
PieceSource::Added => &self.added,
};
s.extend(src[p.start..p.start + p.length].iter());
}
s
}
pub fn piece_count(&self) -> usize {
self.pieces.len()
}
}
pub fn new_piece_table(initial: &str) -> PieceTable {
PieceTable::new(initial)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_content() {
let pt = PieceTable::new("hello");
assert_eq!(pt.as_string(), "hello");
}
#[test]
fn test_len() {
let pt = PieceTable::new("abc");
assert_eq!(pt.len(), 3);
}
#[test]
fn test_is_empty() {
let pt = PieceTable::new("");
assert!(pt.is_empty());
}
#[test]
fn test_insert_at_end() {
let mut pt = PieceTable::new("hello");
pt.insert(5, " world");
assert_eq!(pt.as_string(), "hello world");
}
#[test]
fn test_insert_at_start() {
let mut pt = PieceTable::new("world");
pt.insert(0, "hello ");
assert_eq!(pt.as_string(), "hello world");
}
#[test]
fn test_insert_in_middle() {
let mut pt = PieceTable::new("helo");
pt.insert(3, "l");
assert_eq!(pt.as_string(), "hello");
}
#[test]
fn test_multiple_inserts() {
let mut pt = PieceTable::new("");
pt.insert(0, "c");
pt.insert(0, "b");
pt.insert(0, "a");
assert_eq!(pt.as_string(), "abc");
}
#[test]
fn test_piece_count_grows() {
let mut pt = PieceTable::new("ab");
pt.insert(1, "X");
assert!(pt.piece_count() > 1);
}
#[test]
fn test_new_helper() {
let pt = new_piece_table("test");
assert_eq!(pt.as_string(), "test");
}
#[test]
fn test_insert_empty_string() {
let mut pt = PieceTable::new("abc");
pt.insert(1, "");
assert_eq!(pt.as_string(), "abc");
}
}