Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String.replaceAllMapped with asynchronous result

I am working on a project that requires templates. The main template has an import attribute that specifies the data source. The data is then read and inserted into the string using String.replaceAllMapped. The following code works fine with the File api because it has a readAsStringSync method to read the file synchronously. I now want to read from any arbitrary stream that returns a Future.

How to I make async/await work in this scenario? I have also looked for an async compatible replacement for replaceAllMapped but I have not found a solution that does not require multiple passes with the regex.

Here is a very simplified example of my code:

String loadImports(String content){
  RegExp exp = new RegExp("import=[\"\']([^\"\']*)[\"\']>\\s*<\/");

  return content.replaceAllMapped(exp, (match) {
    String filePath = match.group(1);

    File file = new File(filePath);
    String fileContent = file.readAsStringSync();

    return ">$fileContent</";
  });
}

Example usage:

print(loadImports("<div import='myfragment.txt'></div>"))
like image 452
Zig158 Avatar asked Oct 30 '22 13:10

Zig158


1 Answers

Try this:

Future<String> replaceAllMappedAsync(String string, Pattern exp, Future<String> replace(Match match)) async {
  StringBuffer replaced = new StringBuffer();
  int currentIndex = 0;
  for(Match match in exp.allMatches(string)) {
    String prefix = match.input.substring(currentIndex, match.start);
    currentIndex = match.end;
    replaced
       ..write(prefix)
       ..write(await replace(match));
  }
  replaced.write(string.substring(currentIndex));
  return replaced.toString();
}

To use your example above:

Future<String> loadImports(String content) async {
    RegExp exp = new RegExp("import=[\"\']([^\"\']*)[\"\']>\\s*<\/");

    return replaceAllMappedAsync(content, exp, (match) async {
        String filePath = match.group(1);

        File file = new File(filePath);
        String fileContent = await file.readAsString();
        return ">$fileContent</";
    });
}

And used like this:

loadImports("<div import='myfragment.txt'></div>").then(print);

or, if used in an async function:

print(await loadImports("<div import='myfragment.txt'></div>"));
like image 61
Tonio Avatar answered Nov 26 '22 19:11

Tonio