raekna-storage 0.2.1

This crate provides in-memory and persistent storage functionality to the raekna project.
Documentation
use raekna_common::{EditAction, EditPosition};

use crate::storage::Storage;

pub struct EditHandler<'a> {
    storage: &'a mut Storage,
}

impl<'a> EditHandler<'a> {
    pub fn handle_actions(storage: &'a mut Storage, actions: Vec<EditAction>) {
        let mut handler = EditHandler { storage };
        actions.into_iter().for_each(|action| match action {
            EditAction::NewLine(position) => handler.handle_newline(position),
            EditAction::Insert(position, c) => handler.handle_insert(position, c),
            EditAction::InsertMultiple(position, content) => {
                handler.handle_insert_multiple(position, content)
            }
            EditAction::Delete {
                selection_start,
                selection_end,
            } => handler.handle_delete(selection_start, selection_end),
            EditAction::DeleteForward(position) => handler.handle_delete_forward(position),
        });
    }

    fn line_at<F, R>(&mut self, index: usize, op: F) -> Option<R>
    where
        F: Fn(&mut String) -> R,
    {
        if index < self.storage.lines.len() {
            let line = &mut self.storage.lines[index];
            Some(op(line))
        } else {
            None
        }
    }

    fn handle_newline(&mut self, position: EditPosition) {
        let EditPosition { line, column } = position;
        if column == self.line_at(line, |s| s.len()).unwrap_or(0) {
            self.storage.lines.new_at(line + 1);
        } else if column == 0 {
            self.storage.lines.new_at(line);
        } else {
            let new_line = self.storage.lines[line].split_off(column);
            self.storage.lines.insert(line + 1, new_line);
        }
    }

    fn handle_insert(&mut self, position: EditPosition, c: char) {
        let EditPosition { line, column } = position;
        self.line_at(line, |s| s.insert(column, c));
    }

    fn handle_insert_multiple(&mut self, position: EditPosition, content: String) {
        let EditPosition { mut line, column } = position;
        let remaining = self
            .line_at(line, |s| s[column..].to_owned())
            .unwrap_or_else(|| "".to_owned());
        self.line_at(line, |s| s.truncate(column));
        let mut lines = content.lines();
        if let Some(line_contents) = lines.next() {
            self.line_at(line, |s| s.push_str(line_contents));
        }
        lines.for_each(|line_contents| {
            line += 1;
            self.storage.lines.insert(line, line_contents.to_owned());
        });
        self.line_at(line, |s| s.push_str(&remaining));
    }

    fn handle_delete(
        &mut self,
        selection_start: EditPosition,
        selection_end: Option<EditPosition>,
    ) {
        let start = selection_start;
        match selection_end {
            None => {
                if start.line == 0 && start.column == 0 {
                } else if start.column == 0 {
                    let removed = self.storage.lines.remove(start.line);
                    self.line_at(start.line - 1, |s| s.push_str(&removed.0));
                } else {
                    let to_remove = start.column - 1;
                    self.line_at(start.line, |s| s.remove(to_remove));
                }
            }
            Some(end) => {
                // TODO: Fix
                if start.line == end.line {
                    let end_str = self
                        .line_at(start.line, |s| s[end.column..].to_owned())
                        .unwrap_or_else(|| "".to_owned());
                    self.line_at(start.line, |s| s.replace_range(start.column.., &end_str));
                } else {
                    let end_line = self.storage.lines.remove(end.line);
                    let end_str = &end_line.0[end.column..];
                    self.line_at(start.line, |s| s.replace_range(start.column.., end_str));
                    for i in (start.line + 1..end.line).rev() {
                        self.storage.lines.remove(i);
                    }
                }
            }
        }
    }

    fn handle_delete_forward(&mut self, position: EditPosition) {
        let EditPosition { line, column } = position;
        let is_last_line = line == self.storage.lines.len() - 1;
        let is_last_column = column == self.line_at(line, |s| s.len()).unwrap_or(0);
        if is_last_line && is_last_column {
        } else if column == self.line_at(line, |s| s.len()).unwrap_or(0) {
            let removed = self.storage.lines.remove(line + 1);
            self.line_at(line, |s| s.push_str(&removed.0));
        } else {
            self.line_at(line, |s| s.remove(column));
        }
    }
}