1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::ops::*;

/// An arbitrary constraint limiting the number of operations that can be executed on a buffer.
const MAX_OPS: usize = 1_000_000;
/// An arbitrary constraint limiting the number of characters that can be deleted from a buffer.
const MAX_DELETE_OPS: usize = MAX_OPS * 2;

/// Represents the buffer of characters which are operated on by the commands, and the stack of operations that are eligible for undo commands.
#[derive(Debug)]
pub struct Text {
    /// The underlying buffer which is mutated by the operations applied.
    value: String,
    // The count of operations as declared by the first line of input.
    num_ops: usize,
    // A stack of operations which are eligible for undo.
    operation_stack: Vec<UndoableOperation>,
}

/// ```
/// use simple_text_editor::ops;
/// use simple_text_editor::text::Text;
/// let (num_ops, ops) = ops::parse(r#"4
/// 1 hello
/// 2 1
/// 2 1
/// 1 p me!
/// "#).unwrap();
/// let mut text = Text::new("", num_ops);
/// text.apply(ops);
/// assert_eq!(text.output(), "help me!".to_string());
/// ```
impl Text {
    /// Creates a new `Text` instance, which can start from an initial state instead of an empty buffer.
    pub fn new(init: impl AsRef<str>, num_ops: usize) -> Self {
        Self {
            value: init.as_ref().into(),
            num_ops,
            operation_stack: vec![],
        }
    }

    /// Mutates the `Text` instance by executing each operation in `ops` serially.
    pub fn apply(&mut self, ops: Vec<Operation>) {
        assert!(
            ops.len() <= MAX_OPS,
            format!(
                "The input exceeds the max number of operations permitted. ({})",
                MAX_OPS
            )
        );
        assert!(
            ops.len() == self.num_ops,
            format!("The provided count doesn't match the number of valid operations parsed. (count = {}, valid operations = {})", self.num_ops, ops.len())
        );

        let mut delete_count = 0_usize;
        for op in ops {
            match op {
                // append the value to the inner buffer, and push an undoable operation to the operation stack,
                // along with the data needed to undo: the count of characters to remove from the buffer.
                Operation::Append(val) => {
                    self.value.push_str(val);
                    self.operation_stack
                        .push(UndoableOperation::Append(val.len()));
                }
                // delete `n` characters from the back of the buffer, and push an undoable operation to the
                // operation stack. keep the deleted characters (in order), and include them in the undoable
                // operation so they can be appended later in the undo operation.
                Operation::Delete(n) if n <= self.value.len() => {
                    delete_count += n;
                    assert!(
                        delete_count <= MAX_DELETE_OPS,
                        format!("The input exceeds the max number of characters which can be deleted. ({})", MAX_DELETE_OPS)
                    );

                    // keep the deleted characters so they can be re-appended in the case of an undo command
                    // NOTE: these characters are in reverse order as they were appended, and will need to
                    // be reversed before they are re-appended to the buffer.
                    let mut deleted = vec![];
                    for _ in 0..n {
                        deleted.push(self.value.pop().unwrap() as u8);
                    }

                    self.operation_stack
                        .push(UndoableOperation::Delete(deleted));
                }
                // print out the character at the 1-based index `i` from the buffer.
                Operation::Print(i) if i <= self.value.len() => {
                    println!("{}", self.value.chars().nth(i - 1).unwrap());
                }
                // undo a previous "append" or "delete" operation, maintained by the operation stack.
                // as a reversal of the previous affect, undoing an "append" is to delete the number
                // of appended characters from the back of the buffer, and undoing a "delete" is to
                // append the previously deleted characters to the back of the buffer. the operation
                // to be undone is the element popped from the operation stack.
                Operation::Undo => {
                    if let Some(op) = self.operation_stack.pop() {
                        match op {
                            UndoableOperation::Append(n) => {
                                for _ in 0..n {
                                    self.value.pop();
                                }
                            }
                            UndoableOperation::Delete(mut val) => {
                                // reverse the characters to put them in the correct append order,
                                // and push each back to the buffer. the characters are reversed
                                // here instead of in the Operation::Delete match arm so the work
                                // is deferred as there is no guarantee the undo operation will occur.
                                val.reverse();
                                val.iter().for_each(|c| self.value.push(*c as char));
                            }
                        }
                    }
                }
                _ => {}
            }
        }
    }

    /// Returns the current state of the `Text` instance's internal buffer.
    pub fn output(self) -> String {
        self.value
    }
}