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}