From 1ad4fbd841abb9ced7a6b1225fe309acb27f5def Mon Sep 17 00:00:00 2001
From: penguindark <57967770+penguindark@users.noreply.github.com>
Date: Mon, 3 Jan 2022 05:32:24 +0100
Subject: [PATCH] regex: add a replace_n function (#13016)

---
 vlib/regex/README.md    | 10 ++++++++++
 vlib/regex/regex_test.v | 19 +++++++++++++++++++
 vlib/regex/regex_util.v | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+)

diff --git a/vlib/regex/README.md b/vlib/regex/README.md
index 33a6de5525..067f17d802 100644
--- a/vlib/regex/README.md
+++ b/vlib/regex/README.md
@@ -604,6 +604,16 @@ to use a quick function:
 pub fn (mut re RE) replace_simple(in_txt string, repl string) string
 ```
 
+If it is needed to replace N instances of the found strings it is possible to use:
+```v ignore
+// replace_n return a string where the firts `count` matches are replaced with the repl_str string
+// `count` indicate the number of max replacements that will be done.
+// if count is > 0 the replace began from the start of the string toward the end
+// if count is < 0 the replace began from the end of the string toward the start
+// if count is 0 do nothing
+pub fn (mut re RE) replace_n(in_txt string, repl_str string, count int) string
+```
+
 #### Custom replace function
 
 For complex find and replace operations, you can use `replace_by_fn` .
diff --git a/vlib/regex/regex_test.v b/vlib/regex/regex_test.v
index 8f479b757b..92d911cbd4 100644
--- a/vlib/regex/regex_test.v
+++ b/vlib/regex/regex_test.v
@@ -623,6 +623,25 @@ fn test_regex_func_replace(){
 	assert result == txt2
 }
 
+fn rest_regex_replace_n(){
+	s := "dario 1234 pepep 23454 pera"
+    query := r"\d+"
+
+    mut re := regex.regex_opt(query) or { panic(err) }
+
+    assert re.replace_n(s, "[repl]", 0) == "dario 1234 pepep 23454 pera"
+    assert re.replace_n(s, "[repl]", -1) == "dario 1234 pepep [repl] pera"
+    assert re.replace_n(s, "[repl]", 1) == "dario [repl] pepep 23454 pera"
+    assert re.replace_n(s, "[repl]", 2) == "dario [repl] pepep [repl] pera"
+    assert re.replace_n(s, "[repl]", -2) == "dario [repl] pepep [repl] pera"
+    assert re.replace_n(s, "[repl]", 3) == "dario [repl] pepep [repl] pera"
+    assert re.replace_n(s, "[repl]", -3) == "dario [repl] pepep [repl] pera"
+
+    //mut res := re.replace_n(s, "[repl]", -1)
+    //println("source: ${s}")
+    //println("res   : ${res}")
+}
+
 // test quantifier wrong sequences
 const(
 	test_quantifier_sequences_list = [
diff --git a/vlib/regex/regex_util.v b/vlib/regex/regex_util.v
index 02853ced95..2cb84a51d1 100644
--- a/vlib/regex/regex_util.v
+++ b/vlib/regex/regex_util.v
@@ -463,3 +463,37 @@ pub fn (mut re RE) replace(in_txt string, repl_str string) string {
 	}
 	return res.str()
 }
+
+// replace_n return a string where the firts count matches are replaced with the repl_str string,
+// if count is > 0 the replace began from the start of the string toward the end
+// if count is < 0 the replace began from the end of the string toward the start
+// if count is 0 do nothing
+pub fn (mut re RE) replace_n(in_txt string, repl_str string, count int) string {
+	mut i := 0
+	mut index := 0
+	mut i_p := 0
+	mut res := strings.new_builder(in_txt.len)
+	mut lst := re.find_all(in_txt)
+
+	if count < 0 { // start from the right of the string
+		lst = lst#[count * 2..] // limitate the number of substitions
+	} else if count > 0 { // start from the left of the string
+		lst = lst#[..count * 2] // limitate the number of substitions
+	} else if count == 0 { // no replace
+		return in_txt
+	}
+
+	// println("found: ${lst}")
+	for index < lst.len {
+		i = lst[index]
+		res.write_string(in_txt[i_p..i])
+		res.write_string(repl_str)
+		index++
+		i_p = lst[index]
+		index++
+	}
+	i = i_p
+	res.write_string(in_txt[i..])
+
+	return res.str()
+}