diffmatchpatch 0.0.4

Rewrite of diff-match-patch in Rust
Documentation
//! <https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content>

use diffmatchpatch::*;

pub trait BlockExt {
    fn is_block_start(&self) -> bool;
    fn block_level_prefix(&self) -> &str;
    fn is_whitespace_start(&self) -> bool;
}

impl BlockExt for str {
    fn is_block_start(&self) -> bool {
        self.starts_with("- ") || self.starts_with("-\n")
    }

    fn block_level_prefix(&self) -> &str {
        &self[..self.find('-').unwrap()]
    }

    fn is_whitespace_start(&self) -> bool {
        let c = self.as_bytes()[0];
        c == b'\t' || c == b' ' || c == b'\x09' || c == b'\x0c'
    }
}

pub fn split_blocks(page: &str) -> Vec<&str> {
    let mut blocks = vec![];

    let mut start = 0;
    let mut end = 0;

    let mut it = page.split_inclusive('\n');
    let mut prefix = "";
    loop {
        match it.next() {
            None => {
                if start != end {
                    blocks.push(&page[start..end]);
                }
                break;
            }
            Some(line) => {
                // cont.
                if line.trim().is_empty() {
                    end += line.len();
                } else if line.is_whitespace_start() {
                    if line.starts_with(prefix) {
                        let bare_line = line.trim_start_matches(prefix);
                        if bare_line.is_block_start() {
                            // new block one the same level
                            if start != end {
                                blocks.push(&page[start..end]);
                            }
                            start = end;
                            end = start + line.len();
                        } else if bare_line.trim_start().is_block_start() {
                            // new block on sub level
                            if start != end {
                                blocks.push(&page[start..end]);
                            }
                            start = end;
                            end = start + line.len();
                            prefix = line.block_level_prefix()
                        } else {
                            end += line.len();
                        }
                    } else if line.trim_start().is_block_start() {
                        // new block on parent level
                        if start != end {
                            blocks.push(&page[start..end]);
                        }
                        start = end;
                        end = start + line.len();
                        prefix = line.block_level_prefix();
                    } else {
                        // block cont.
                        end += line.len();
                    }
                } else if line.is_block_start() {
                    // new block
                    if start != end {
                        blocks.push(&page[start..end]);
                    }
                    start = end;
                    end = start + line.len();
                    prefix = "";
                } else if prefix.is_empty() && line.contains(":: ") {
                    // global property line
                    if start != end {
                        blocks.push(&page[start..end]);
                    }
                    start = end;
                    end = start + line.len();
                    prefix = "";
                } else {
                    end += line.len();
                }
            }
        }
    }
    blocks
}

fn diff_blocks<'a>(
    base_blocks: &'a [&'a str],
    new_blocks: &'a [&'a str],
) -> Vec<Diff<Vec<&'a str>>> {
    // diff_blocksToChars then diff_blocksToUniqueId

    let dmp = DiffMatchPatch::new();
    let (ac, bc, item_array) = dmp.diff_any_to_chars(base_blocks, new_blocks);
    let mut diffs = dmp.diff_main(&ac, &bc, false);

    let diffs = dmp.diff_chars_to_any(&diffs, &item_array);

    diffs
}

fn merge_blocks<'a>(base_blocks: &'a [&'a str], branch_a: &'a [&'a str], branch_b: &'a [&'a str]) {
    //let mut diff_pool = vec![];
    // step 1, merge diffs
    for (i, diff) in diff_blocks(base_blocks, branch_a).into_iter().enumerate() {
        todo!();
    }
}

fn main() {
    let a = r"- Block 1
    - Block 2
        - Block 2.1
    - Block 3
- Block 4
    - Block 5";
    let b = r"- Block 1
    - Block 4
        - Block 2.1
    - Block 3
- Block 5
    - Block 6";

    let base = include_str!("base.md");
    let a = include_str!("a.md");
    let b = include_str!("b.md");

    //let ab = split_blocks(a);
    //let bb = split_blocks(b);

    // let diffs = diff_blocks(&ab, &bb);
    // println!("diffs => {:#?}", diffs);
    // todo!();

    let mut dmp = DiffMatchPatch::new();

    //let (ac, bc, item_array) = dmp.diff_any_to_chars(&ab, &bb);
    let ac = base.to_chars();
    let bc = a.to_chars();

    let patches = dmp.patch_make1(&ac, &bc);
    println!("patches => {:#?}", patches);

    for p in patches {
        println!("======");
        println!("{}", p);
    }

    todo!();

    // println!("{ac:?} {bc:?}\n{item_array:#?}");
    let mut diffs = dmp.diff_main(&ac, &bc, false);
    dmp.diff_cleanup_efficiency(&mut diffs);

    //let new_diffs = dmp.diff_chars_to_any(&mut diffs, &item_array);

    //println!("=> {new_diffs:#?}");
    // println!("diffs => {}", new_diffs.len());
}