spidior 0.2.2

A tool for handling sed-like substitution tasks where pesky source code semantics are getting in the way.
use std::error::Error;

use textbuffer::TextBuffer;

use super::{matcher::find, queryengine::QueryEngine};
use crate::nfa::matcher::Match;
use crate::{
    editing::textbuffer,
    languages::clike::Clike,
    regexparser::ast::{Replace, Replacement},
};

pub type Acceptor = fn(&str, &str) -> bool;

pub fn replace(
    path_name: &String,
    input: &String,
    replacement: Replace,
    acceptor: Acceptor,
) -> Result<String, Box<dyn Error>> {
    let mut qe = QueryEngine::build(input, Box::new(Clike {}), Box::new(Clike {}));
    let matches = find(&mut qe, &input, replacement.clone().find);
    let mut tb = TextBuffer::new();
    let mut offset: i32 = 0;
    tb.add(input);
    for m in matches {
        let r = replace_to_string(&replacement.replace, &m, input);
        let start = (m.start() as i32 + offset) as usize;
        let to_replace = tb.get(start, m.len())?;

        let withinfunction = replacement.location.check(&input, start, path_name, &qe);

        if withinfunction && acceptor(&to_replace, &r) {
            tb.replace(start, m.len(), &r)?;
            offset += r.len() as i32 - m.len() as i32;
        }
    }
    Ok(tb.consume())
}

fn replace_to_string(replacement: &Replacement, m: &Match, s: &String) -> String {
    let mut ret = String::new();
    for ri in &replacement.replacements {
        match ri {
            crate::regexparser::ast::ReplaceItem::String(s) => {
                ret += &s;
            }
            crate::regexparser::ast::ReplaceItem::BackRef(x) => {
                ret += &m.get_group(*x as usize, s);
            }
        }
    }
    ret
}

#[test]
fn test_replace_to_string() -> Result<(), Box<dyn std::error::Error>> {
    let m = Match::new(0, 0, vec![]);
    let x = Replacement {
        replacements: vec![
            crate::regexparser::ast::ReplaceItem::String("hello".into()),
            crate::regexparser::ast::ReplaceItem::String(" ".into()),
            crate::regexparser::ast::ReplaceItem::String("world".into()),
            crate::regexparser::ast::ReplaceItem::String("!".into()),
        ],
    };
    assert_eq!(replace_to_string(&x, &m, &"".to_string()), "hello world!");
    Ok(())
}

#[test]
fn test_replace() -> Result<(), Box<dyn std::error::Error>> {
    use crate::regexparser;
    let regex = regexparser::parse("%s/bill/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoe".into(), regex, |_, _| true)?, "joejoe");

    let regex = regexparser::parse("%s/bob|joe|e*/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoe".into(), regex, |_, _| true)?, "bobbob");

    let regex = regexparser::parse("%s/bob|joe|e*/jack/g")?;
    assert_eq!(replace(&"".into(), &"joee".into(), regex, |_, _| true)?, "jackjack");

    let regex = regexparser::parse("%s/bob|joe|e*/o/g")?;
    assert_eq!(replace(&"".into(), &"joeejoe".into(), regex, |_, _| true)?, "ooo");

    let regex = regexparser::parse("%s/(joe)*/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoejoejo".into(), regex, |_, _| true)?, "bobjo");

    let regex = regexparser::parse("%s/(joe)*/bob/g")?;
    assert_eq!(replace(&"".into(), &"eee".into(), regex, |_, _| true)?, "eee");

    let regex = regexparser::parse("%s/jo*e/bob/g")?;
    assert_eq!(
        replace(&"".into(), &"jejoejooeej".into(), regex, |_, _| true)?,
        "bobbobbobej"
    );

    let regex = regexparser::parse("%s/jo+e/bob/g")?;
    assert_eq!(
        replace(&"".into(), &"jejoejooeej".into(), regex, |_, _| true)?,
        "jebobbobej"
    );

    let regex = regexparser::parse("%s/[a-z]*/bob/g")?;
    assert_eq!(replace(&"".into(), &"-2607".into(), regex, |_, _| true)?, "-2607");

    let regex = regexparser::parse("%s/[a-z]*/bob/g")?;
    assert_eq!(
        replace(&"".into(), &"-2e6f0z7a".into(), regex, |_, _| true)?,
        "-2bob6bob0bob7bob"
    );

    let regex = regexparser::parse("%s/[^a-z]*/bob/g")?;
    assert_eq!(replace(&"".into(), &"joe".into(), regex, |_, _| true)?, "joe");

    let regex = regexparser::parse("%s/[^a-z]*/bob/g")?;
    assert_eq!(replace(&"".into(), &"2607".into(), regex, |_, _| true)?, "bob");
    Ok(())
}
#[test]
fn test_replace_backref() -> Result<(), Box<dyn std::error::Error>> {
    use crate::regexparser;
    let regex = regexparser::parse("%s/(1)/\\1\\1/g")?;
    assert_eq!(replace(&"".into(), &"1".into(), regex, |_, _| true)?, "11");
    Ok(())
}

#[test]
fn test_pos() -> Result<(), Box<dyn std::error::Error>> {
    use crate::regexparser;
    let regex = regexparser::parse("%s/[[pos=0:3]]/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoe".into(), regex, |_, _| true)?, "bobjoe");
    let regex = regexparser::parse("%s/[[pos=1:3]]/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoe".into(), regex, |_, _| true)?, "jboboe");
    let regex = regexparser::parse("%s/[[pos=2:1]]joe/bob/g")?;
    assert_eq!(replace(&"".into(), &"joejoe".into(), regex, |_, _| true)?, "jobob");
    Ok(())
}