'Using String.replace to replace a multi-line section of the string in Rust

I am trying to use Rust to go through and make changes to an HTML file. Basically, I find specific things in the code that I want replaced, then I replace it with something else using String.replace(stringToReplace, replacingString). This has been working well until I tried to replace a multi-line section of it. For example:

index.html

<div id="outer">
    <h1>Some title</h1>
    <p>Some text</p>
</div>

newIndex.html

<p>I have replaced the whole div</p>

No matter what I have tried, I cannot get the string to match for multiple lines. It just simply never replaces it. How do I go about matching on that multi-line part of the code. I thought about doing each line individually, but I obviously can't do that because replacing </div> would not be good.

Below is the code I am using. I cannot make any changes to the original code to make it easier to match. In main.rs I am looping through Change.changes the even indices are the string to replace and the odd indices are the replacing text.

main.rs

use std::fs::File;
use std::io::prelude::*;

mod ejs;
mod js;
mod classes;
mod controllers;
mod change;

fn main() {
    let ejs_files: Vec<change::Change> = ejs::change();
    let js_files: Vec<change::Change> = js::change();
    let class_files: Vec<change::Change> = classes::change();
    let controller_files: Vec<change::Change> = controllers::change();

    for i in 0..ejs_files.len() {
        let mut contents = match read_file(&ejs_files[i].location) {
            Ok(contents) => contents,
            Err(e) => panic!(e)
        };

        let mut j = 0;
        while j < ejs_files[i].changes.len() {
            contents = contents.replace(ejs_files[i].changes[j], ejs_files[i].changes[j+1]);
            j += 2;
        }

        match write_file(&ejs_files[i].location, contents) {
            Err(e) => panic!(e),
            _ => ()
        };
    }

    for i in 0..js_files.len() {
        let mut contents = match read_file(&js_files[i].location) {
            Ok(contents) => contents,
            Err(e) => panic!(e)
        };

        let mut j = 0;
        while j < js_files[i].changes.len() {
            contents = contents.replace(js_files[i].changes[j], js_files[i].changes[j+1]);
            j += 2;
        }

        match write_file(&js_files[i].location, contents) {
            Err(e) => panic!(e),
            _ => ()
        };
    }

    for i in 0..class_files.len() {
        let mut contents = match read_file(&class_files[i].location) {
            Ok(contents) => contents,
            Err(e) => panic!(e)
        };

        let mut j = 0;
        while j < class_files[i].changes.len() {
            contents = contents.replace(class_files[i].changes[j], class_files[i].changes[j+1]);
            j +=2;
        }

        match write_file(&class_files[i].location, contents) {
            Err(e) => panic!(e),
            _ => ()
        };
    }

    for i in 0..controller_files.len() {
        let mut contents = match read_file(&controller_files[i].location) {
            Ok(contents) => contents,
            Err(e) => panic!(e)
        };

        let mut j = 0;
        while j < controller_files[i].changes.len() {
            contents = contents.replace(controller_files[i].changes[j], controller_files[i].changes[j+1]);
            j +=2;
        }

        match write_file(&controller_files[i].location, contents) {
            Err(e) => panic!(e),
            _ => ()
        };
    }
}

fn read_file(file: &str) -> std::io::Result<String> {
    let mut file = File::open(file)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

fn write_file(file: &str, contents: String) -> std::io::Result<()> {
    let mut file = File::create(file)?;
    file.write(contents.as_bytes())?;
    Ok(())
}

change.rs

pub struct Change<'a> {
    pub location: String,
    pub changes: Vec<&'a str>
}

js.rs (short example):

use crate::change::Change;

pub fn change<'a>() -> Vec<Change<'a>> {
    let folder_location: &str = "../../javascript/InventoryManagement/";

    let home_js = Change {
        location: format!("{}{}", folder_location, "views/dashboardPage/js/strands/home.js"),
        changes: vec![
            "% vs last month", "% по сравнению с прошлым месяцем",
            "REVENUE", "ДОХОД",
            "\"DATE\"", "\"ДАТА\"",
            "MOST POPULAR INGREDIENTS", "САМЫЕ ПОПУЛЯРНЫЕ ИНГРЕДИЕНТЫ",
            "QUANTITY", "КОЛИЧЕСТВО"
        ]
    };
    

In main.rs I am going through different files and replacing certain things. JS, HTML(EJS) and eventually CSS. Making the changes works well until I have to make a multi-line change. If I do something like this then it doesn't work.

js.rs (multi-line):

use crate::change::Change;

pub fn change<'a>() -> Vec<Change<'a>> {
    let folder_location: &str = "../../javascript/InventoryManagement/";

    let home_js = Change {
        location: format!("{}{}", folder_location, "views/dashboardPage/js/strands/home.js"),
        changes: vec![
            "<div id='something'>
                <p>My multi-line comment</p>
           </div>", 
           "<p>Something else</p>"
        ]
    };
    vec![home_js]
}

If there is a better way to do this, then I would love to know as well. Or just a way to find and replace multi-line parts of the code. I am pretty new to Rust and this is the first project I have done with it.



Solution 1:[1]

You could use regex crate and write a function. it replaces anything that matches the given text.

use regex::Regex;

fn replace(target: &str, replaceWith: &str, text: &str)-> Result<String, regex::Error>
  {
    let regex = Regex::new(target)?;
    Ok(regex.replace_all(text, replaceWith).to_string())
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yilmaz