ppatch 0.1.0

A library to search/replace binary data via patterns
Documentation
use ppatch::prelude::*;
use ppatch::PatternSearchType;
use std::io::Write;
use std::str::FromStr;

// Returns index of first mismatch or len of smaller slice
fn slice_equality(one: &[u8], other: &[u8]) -> usize {
    let mut index = 0;

    for pair in one.iter().zip(other.iter()) {
        if pair.0 != pair.1 {
            return index;
        }
        index += 1;
    }

    index
}

const INPUT: [u8; 168] = [
    0x50, 0xF0, 0x01, 0x00, 0xDF, 0xF8, 0x1C, 0x1C, 0x08, 0x70, 0x66, 0x48, 0x00, 0x78, 0x40, 0x1C,
    0x64, 0x49, 0x08, 0x70, 0x47, 0x48, 0x00, 0x68, 0xDF, 0xF8, 0x0C, 0x1C, 0x08, 0x42, 0x17, 0xD1,
    0x45, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD1, 0x00, 0x21, 0x08, 0x20, 0x00, 0xF0, 0xA6, 0xFF,
    0x0E, 0xE0, 0x41, 0x48, 0x00, 0x78, 0x01, 0x28, 0x0A, 0xD1, 0x00, 0x21, 0x14, 0x20, 0x00, 0xF0,
    0x9D, 0xFF, 0xDF, 0xF8, 0xE8, 0x0B, 0x00, 0x88, 0x40, 0x1C, 0xDF, 0xF8, 0xE0, 0x1B, 0x08, 0x80,
    0xDF, 0xF8, 0xDC, 0x0B, 0x00, 0x88, 0x40, 0x1C, 0xDF, 0xF8, 0xD4, 0x1B, 0x08, 0x80, 0x45, 0xE0,
    0xDF, 0xF8, 0xB8, 0x0B, 0x00, 0x88, 0xB0, 0xF5, 0x50, 0x4F, 0x05, 0xD0, 0xDF, 0xF8, 0xAC, 0x0B,
    0x00, 0x88, 0xB0, 0xF5, 0x30, 0x5F, 0x39, 0xD1, 0xDF, 0xF8, 0xA4, 0x0B, 0x00, 0x78, 0x00, 0x28,
    0x34, 0xD1, 0x47, 0x48, 0xFF, 0x21, 0x01, 0x70, 0xDF, 0xF8, 0x98, 0x0B, 0x00, 0x78, 0x50, 0xF0,
    0x01, 0x00, 0xDF, 0xF8, 0x90, 0x1B, 0x08, 0x70, 0x42, 0x48, 0x00, 0x78, 0x40, 0x1E, 0x41, 0x49,
    0x08, 0x70, 0x24, 0x48, 0x00, 0x68, 0xDF, 0xF8,
];

const RESULT: [u8; 168] = [
    0x50, 0xF0, 0x01, 0x00, 0xDF, 0xF8, 0x1C, 0x1C, 0x08, 0x70, 0x66, 0x48, 0x00, 0x78, 0x80, 0x1C,
    0x64, 0x49, 0x08, 0x70, 0x47, 0x48, 0x00, 0x68, 0xDF, 0xF8, 0x0C, 0x1C, 0x08, 0x42, 0x17, 0xD1,
    0x45, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD1, 0x00, 0x21, 0x08, 0x20, 0x00, 0xF0, 0xA6, 0xFF,
    0x0E, 0xE0, 0x41, 0x48, 0x00, 0x78, 0x01, 0x28, 0x0A, 0xD1, 0x00, 0x21, 0x14, 0x20, 0x00, 0xF0,
    0x9D, 0xFF, 0xDF, 0xF8, 0xE8, 0x0B, 0x00, 0x88, 0x40, 0x1C, 0xDF, 0xF8, 0xE0, 0x1B, 0x08, 0x80,
    0xDF, 0xF8, 0xDC, 0x0B, 0x00, 0x88, 0x40, 0x1C, 0xDF, 0xF8, 0xD4, 0x1B, 0x08, 0x80, 0x45, 0xE0,
    0xDF, 0xF8, 0xB8, 0x0B, 0x00, 0x88, 0xB0, 0xF5, 0x50, 0x4F, 0x05, 0xD0, 0xDF, 0xF8, 0xAC, 0x0B,
    0x00, 0x88, 0xB0, 0xF5, 0x30, 0x5F, 0x39, 0xD1, 0xDF, 0xF8, 0xA4, 0x0B, 0x00, 0x78, 0x00, 0x28,
    0x34, 0xD1, 0x47, 0x48, 0xFF, 0x21, 0x01, 0x70, 0xDF, 0xF8, 0x98, 0x0B, 0x00, 0x78, 0x50, 0xF0,
    0x01, 0x00, 0xDF, 0xF8, 0x90, 0x1B, 0x08, 0x70, 0x42, 0x48, 0x00, 0x78, 0x80, 0x1E, 0x41, 0x49,
    0x08, 0x70, 0x24, 0x48, 0x00, 0x68, 0xDF, 0xF8,
];

const SEARCH_STRING: &str =
    "0x08 0x70 0x?? 0x48 0x00 0x78 0x40 0x?? 0x?? 0x49 0x08 0x70 0x?? 0x48 0x00 0x68";
const REPLACE_STRING: &str = "0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x80";

#[test]
fn test_multi() {
    let search = ppatch::Pattern::from_str(SEARCH_STRING).unwrap();
    let replace = ppatch::Pattern::from_str(REPLACE_STRING).unwrap();

    let mut writer = Vec::new();

    for item in INPUT
        .into_iter()
        .search_pattern(&search) // pattern_search_ref_iterator
        .inspect(|result| match result {
            PatternSearchType::Match { ref data, index } => {
                let input_slice = &INPUT[*index..(index + search.len())];
                assert_eq!(data.len(), input_slice.len());
                assert_eq!(slice_equality(&data, input_slice), data.len());
            }
            PatternSearchType::NonMatch(_) => {}
        })
        .replace_pattern(&replace) // pattern_replace_iterator
        .inspect(|result| match result {
            Ok(_) => {}
            Err(error) => assert!(false, "{}", error),
        })
        .search_pattern(&search) // pattern_search_result_iterator
        .inspect(|result| match result {
            Ok(search_type) => {
                if let PatternSearchType::Match { data, index } = search_type {
                    assert!(
                        false,
                        "There should be no match on second round: {:#X} {:X?}",
                        index, data
                    );
                }
            }
            Err(error) => assert!(false, "{}", error),
        })
        .replace_pattern(&replace)
    // pattern_replace_result_iterator
    {
        match item {
            Err(error) => {
                assert!(false, "{}", error);
            }
            Ok(value) => {
                let _ = writer.write_all(&[value]);
            }
        }
    }

    assert_eq!(writer.len(), RESULT.len());
    assert_eq!(slice_equality(&writer, &RESULT), RESULT.len());
}