diff-rs 0.2.3

Rust diff implementation
Documentation
//!
//! The Diff library.
//!

use std::{collections::VecDeque, io};

pub fn diff(
    text1: &[String],
    text2: &[String],
    context_radius: usize,
) -> io::Result<Vec<String>> {
    let mut processor = Processor::new(&text1, &text2, context_radius);
    {
        let mut replace = diffs::Replace::new(&mut processor);
        diffs::myers::diff(&mut replace, &text1, &text2)?;
    }
    Ok(processor.result())
}

struct Processor<'a> {
    text1: &'a [String],
    text2: &'a [String],

    context_radius: usize,
    inserted: usize,
    removed: usize,

    context: Context,
    result: Vec<String>,
}

impl<'a> Processor<'a> {
    pub fn new(text1: &'a [String], text2: &'a [String], context_radius: usize) -> Self {
        Self {
            text1,
            text2,

            context_radius,
            inserted: 0,
            removed: 0,

            context: Context::new(),
            result: Vec::new(),
        }
    }

    pub fn result(self) -> Vec<String> {
        self.result
    }
}

struct Context {
    pub start: Option<usize>,
    pub data: VecDeque<String>,
    pub changed: bool,

    pub counter: usize,
    pub equaled: usize,
    pub removed: usize,
    pub inserted: usize,
}

impl Context {
    pub fn new() -> Self {
        Self {
            start: None,
            data: VecDeque::new(),
            changed: false,

            counter: 0,
            equaled: 0,
            removed: 0,
            inserted: 0,
        }
    }

    pub fn to_vec(&self, removed: usize, inserted: usize) -> Vec<String> {
        let mut start = if let Some(start) = self.start {
            start
        } else {
            return Vec::new();
        };
        if start == 0 {
            start = 1;
        }
        let mut data = Vec::with_capacity(self.data.len() + 1);
        if self.changed {
            data.push(format!(
                "@@ -{},{} +{},{} @@",
                start,
                self.equaled + self.removed,
                start + inserted - removed,
                self.equaled + self.inserted,
            ));
            for s in self.data.iter() {
                data.push(s.to_owned());
            }
        }
        data
    }
}

impl<'a> diffs::Diff for Processor<'a> {
    type Error = io::Error;

    fn equal(&mut self, old: usize, _new: usize, len: usize) -> Result<(), Self::Error> {
        if self.context.start.is_none() {
            self.context.start = Some(old);
        }

        self.context.counter = 0;
        for i in old..old + len {
            if !self.context.changed {
                if self.context.counter < self.context_radius {
                    self.context.data.push_back(format!(" {}", self.text1[i]));
                    self.context.equaled += 1;
                    self.context.counter += 1;
                }
                if self.context.counter >= self.context_radius {
                    self.context.data.push_back(format!(" {}", self.text1[i]));
                    self.context.data.pop_front();
                    if let Some(ref mut start) = self.context.start {
                        *start += 1;
                    }
                    self.context.counter += 1;
                }
            }
            if self.context.changed {
                if self.context.counter < self.context_radius * 2 {
                    self.context.data.push_back(format!(" {}", self.text1[i]));
                    self.context.equaled += 1;
                    self.context.counter += 1;
                }
                if self.context.counter == self.context_radius && len > self.context_radius * 2 {
                    self.result
                        .append(&mut self.context.to_vec(self.removed, self.inserted));

                    let mut context = Context::new();
                    for _ in 0..self.context_radius {
                        context.data.push_back(String::new());
                    }
                    context.counter = self.context_radius;
                    context.equaled = self.context_radius;
                    context.start = Some(i - 1);

                    self.removed += self.context.removed;
                    self.inserted += self.context.inserted;
                    self.context = context;
                }
            }
        }

        Ok(())
    }

    fn delete(&mut self, old: usize, len: usize) -> Result<(), Self::Error> {
        if self.context.start.is_none() {
            self.context.start = Some(old);
        }

        for i in old..old + len {
            self.context.data.push_back(format!("-{}", self.text1[i]));
        }
        self.context.changed = true;
        self.context.removed += len;

        Ok(())
    }

    fn insert(&mut self, old: usize, new: usize, new_len: usize) -> Result<(), Self::Error> {
        if self.context.start.is_none() {
            self.context.start = Some(old);
        }

        for i in new..new + new_len {
            self.context.data.push_back(format!("+{}", self.text2[i]));
        }
        self.context.changed = true;
        self.context.inserted += new_len;

        Ok(())
    }

    fn replace(
        &mut self,
        old: usize,
        old_len: usize,
        new: usize,
        new_len: usize,
    ) -> Result<(), Self::Error> {
        if self.context.start.is_none() {
            self.context.start = Some(old);
        }

        for i in old..old + old_len {
            self.context.data.push_back(format!("-{}", self.text1[i]));
        }
        for i in new..new + new_len {
            self.context.data.push_back(format!("+{}", self.text2[i]));
        }
        self.context.changed = true;
        self.context.removed += old_len;
        self.context.inserted += new_len;

        Ok(())
    }

    fn finish(&mut self) -> Result<(), Self::Error> {
        if self.context.counter > self.context_radius {
            let truncation = self.context.counter - self.context_radius;
            if self.context.data.len() > truncation {
                let new_size = self.context.data.len() - truncation;
                self.context.equaled -= truncation;
                self.context.data.truncate(new_size);
            }
        }
        self.result
            .append(&mut self.context.to_vec(self.removed, self.inserted));
        Ok(())
    }
}