atac 0.11.2

Arguably a Terminal API Client. Feature-full, free, open-source, offline and account-less.
#[derive(Default)]
pub struct TextInput {
    pub text: String,
    pub cursor_position: usize,
}


impl TextInput {
    pub fn move_cursor_left(&mut self) {
        let cursor_moved_left = self.cursor_position.saturating_sub(1);
        self.cursor_position = self.clamp_cursor(cursor_moved_left);
    }

    pub fn move_cursor_right(&mut self) {
        let cursor_moved_right = self.cursor_position.saturating_add(1);
        self.cursor_position = self.clamp_cursor(cursor_moved_right);
    }

    pub fn enter_char(&mut self, new_char: char) {
        if !new_char.is_ascii() {
            return;
        }

        self.text.insert(self.cursor_position, new_char);
        self.move_cursor_right();
    }

    pub fn enter_str(&mut self, string: &str) {
        for char in string.chars() {
            self.enter_char(char)
        }
    }

    pub fn delete_char_backward(&mut self) {
        let is_not_cursor_leftmost = self.cursor_position != 0;
        if is_not_cursor_leftmost {
            // Method "remove" is not used on the saved text for deleting the selected char.
            // Reason: Using remove on String works on bytes instead of the chars.
            // Using remove would require special care because of char boundaries.

            let current_index = self.cursor_position;
            let from_left_to_current_index = current_index - 1;

            // Getting all characters before the selected character.
            let before_char_to_delete = self.text.chars().take(from_left_to_current_index);
            // Getting all characters after selected character.
            let after_char_to_delete = self.text.chars().skip(current_index);

            // Put all characters together except the selected one.
            // By leaving the selected one out, it is forgotten and therefore deleted.
            self.text = before_char_to_delete.chain(after_char_to_delete).collect();
            self.move_cursor_left();
        }
    }

    pub fn delete_char_forward(&mut self) {
        let is_not_cursor_rightmost = self.cursor_position != self.text.len();

        if is_not_cursor_rightmost {
            // Method "remove" is not used on the saved text for deleting the selected char.
            // Reason: Using remove on String works on bytes instead of the chars.
            // Using remove would require special care because of char boundaries.

            let current_index = self.cursor_position;

            // Getting all characters before the selected character.
            let before_char_to_delete = self.text.chars().take(current_index);
            // Getting all characters after selected character.
            let after_char_to_delete = self.text.chars().skip(current_index + 1);

            // Put all characters together except the selected one.
            // By leaving the selected one out, it is forgotten and therefore deleted.
            self.text = before_char_to_delete.chain(after_char_to_delete).collect();
        }
    }

    pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
        new_cursor_pos.clamp(0, self.text.len())
    }

    pub fn reset_cursor(&mut self) {
        self.cursor_position = 0;
    }

    pub fn reset_input(&mut self) {
        self.text.clear();
        self.reset_cursor();
    }
}