simple_text_editor/text.rs
1use crate::ops::*;
2
3/// An arbitrary constraint limiting the number of operations that can be executed on a buffer.
4const MAX_OPS: usize = 1_000_000;
5/// An arbitrary constraint limiting the number of characters that can be deleted from a buffer.
6const MAX_DELETE_OPS: usize = MAX_OPS * 2;
7
8/// Represents the buffer of characters which are operated on by the commands, and the stack of operations that are eligible for undo commands.
9#[derive(Debug)]
10pub struct Text {
11 /// The underlying buffer which is mutated by the operations applied.
12 value: String,
13 // The count of operations as declared by the first line of input.
14 num_ops: usize,
15 // A stack of operations which are eligible for undo.
16 operation_stack: Vec<UndoableOperation>,
17}
18
19/// ```
20/// use simple_text_editor::ops;
21/// use simple_text_editor::text::Text;
22/// let (num_ops, ops) = ops::parse(r#"4
23/// 1 hello
24/// 2 1
25/// 2 1
26/// 1 p me!
27/// "#).unwrap();
28/// let mut text = Text::new("", num_ops);
29/// text.apply(ops);
30/// assert_eq!(text.output(), "help me!".to_string());
31/// ```
32impl Text {
33 /// Creates a new `Text` instance, which can start from an initial state instead of an empty buffer.
34 pub fn new(init: impl AsRef<str>, num_ops: usize) -> Self {
35 Self {
36 value: init.as_ref().into(),
37 num_ops,
38 operation_stack: vec![],
39 }
40 }
41
42 /// Mutates the `Text` instance by executing each operation in `ops` serially.
43 pub fn apply(&mut self, ops: Vec<Operation>) {
44 assert!(
45 ops.len() <= MAX_OPS,
46 format!(
47 "The input exceeds the max number of operations permitted. ({})",
48 MAX_OPS
49 )
50 );
51 assert!(
52 ops.len() == self.num_ops,
53 format!("The provided count doesn't match the number of valid operations parsed. (count = {}, valid operations = {})", self.num_ops, ops.len())
54 );
55
56 let mut delete_count = 0_usize;
57 for op in ops {
58 match op {
59 // append the value to the inner buffer, and push an undoable operation to the operation stack,
60 // along with the data needed to undo: the count of characters to remove from the buffer.
61 Operation::Append(val) => {
62 self.value.push_str(val);
63 self.operation_stack
64 .push(UndoableOperation::Append(val.len()));
65 }
66 // delete `n` characters from the back of the buffer, and push an undoable operation to the
67 // operation stack. keep the deleted characters (in order), and include them in the undoable
68 // operation so they can be appended later in the undo operation.
69 Operation::Delete(n) if n <= self.value.len() => {
70 delete_count += n;
71 assert!(
72 delete_count <= MAX_DELETE_OPS,
73 format!("The input exceeds the max number of characters which can be deleted. ({})", MAX_DELETE_OPS)
74 );
75
76 // keep the deleted characters so they can be re-appended in the case of an undo command
77 // NOTE: these characters are in reverse order as they were appended, and will need to
78 // be reversed before they are re-appended to the buffer.
79 let mut deleted = vec![];
80 for _ in 0..n {
81 deleted.push(self.value.pop().unwrap() as u8);
82 }
83
84 self.operation_stack
85 .push(UndoableOperation::Delete(deleted));
86 }
87 // print out the character at the 1-based index `i` from the buffer.
88 Operation::Print(i) if i <= self.value.len() => {
89 println!("{}", self.value.chars().nth(i - 1).unwrap());
90 }
91 // undo a previous "append" or "delete" operation, maintained by the operation stack.
92 // as a reversal of the previous affect, undoing an "append" is to delete the number
93 // of appended characters from the back of the buffer, and undoing a "delete" is to
94 // append the previously deleted characters to the back of the buffer. the operation
95 // to be undone is the element popped from the operation stack.
96 Operation::Undo => {
97 if let Some(op) = self.operation_stack.pop() {
98 match op {
99 UndoableOperation::Append(n) => {
100 for _ in 0..n {
101 self.value.pop();
102 }
103 }
104 UndoableOperation::Delete(mut val) => {
105 // reverse the characters to put them in the correct append order,
106 // and push each back to the buffer. the characters are reversed
107 // here instead of in the Operation::Delete match arm so the work
108 // is deferred as there is no guarantee the undo operation will occur.
109 val.reverse();
110 val.iter().for_each(|c| self.value.push(*c as char));
111 }
112 }
113 }
114 }
115 _ => {}
116 }
117 }
118 }
119
120 /// Returns the current state of the `Text` instance's internal buffer.
121 pub fn output(self) -> String {
122 self.value
123 }
124}