clankerdiff-protocol 0.3.3

Versioned subprocess and live session protocols for Clankerdiff
Documentation
use crate::{
    server::{ServerEvent, ServerMessage},
    shared::{DiffSnapshot, DocumentUpdate, FileEntry, ProtocolError},
};
use clankerdiff_core::{DiffDocument, RepoPath};
use std::{collections::BTreeMap, sync::Arc};

#[derive(Debug, Default)]
pub struct DocumentCache {
    document: Arc<DiffDocument>,
    index: BTreeMap<RepoPath, usize>,
}

impl DocumentCache {
    pub fn decode_event(&mut self, text: &str) -> Result<ServerEvent, ProtocolError> {
        self.apply_event(ServerMessage::decode(text)?)
    }

    pub fn apply_event(&mut self, message: ServerMessage) -> Result<ServerEvent, ProtocolError> {
        message.try_map(|update| Ok(Arc::new(self.apply(&update)?)))
    }

    pub fn apply(&mut self, update: &DocumentUpdate) -> Result<DiffSnapshot, ProtocolError> {
        let mut files = Vec::with_capacity(update.files.len());
        let mut index = BTreeMap::new();
        for entry in &update.files {
            let file = match entry {
                FileEntry::Unchanged(path) => self
                    .index
                    .get(path)
                    .and_then(|index| self.document.files.get(*index))
                    .ok_or(ProtocolError::Files)?
                    .clone(),
                FileEntry::Changed(file) => file.as_ref().clone(),
            };
            if index.insert(file.path.clone(), files.len()).is_some() {
                return Err(ProtocolError::Files);
            }
            files.push(file);
        }
        self.document = Arc::new(DiffDocument {
            repo_root: update.repo_root.clone(),
            files,
        });
        self.index = index;
        Ok(DiffSnapshot {
            scope: update.scope,
            document: Arc::clone(&self.document),
        })
    }
}