@@ -15,6 +15,7 @@ package strings
1515
1616import (
1717 "html/template"
18+ stds "strings"
1819 "testing"
1920
2021 qt "github.com/frankban/quicktest"
@@ -375,6 +376,91 @@ func TestReplace(t *testing.T) {
375376 }
376377}
377378
379+ func TestReplacePairs (t * testing.T ) {
380+ t .Parallel ()
381+ c := qt .New (t )
382+
383+ for _ , test := range []struct {
384+ args []any
385+ expect string
386+ }{
387+ // slice form
388+ {[]any {[]string {"a" , "b" }, "aab" }, "bbb" },
389+ {[]any {[]string {"a" , "b" , "b" , "c" }, "aab" }, "bbc" },
390+ {[]any {[]string {"app" , "pear" , "apple" , "orange" }, "apple" }, "pearle" },
391+ {[]any {[]string {}, "aab" }, "aab" },
392+ {[]any {[]string {"remove-me" , "" }, "text remove-me" }, "text " },
393+ {[]any {[]string {"" , "X" }, "ab" }, "XaXbX" },
394+ {[]any {[]string {"a" , "b" }, template .HTML ("aab" )}, "bbb" }, // template.HTML source
395+ {[]any {[]string {"a" , "b" }, 42 }, "42" }, // int source (cast: 42→"42")
396+ {[]any {[]any {"a" , "b" }, "s" }, "s" }, // []any with all strings
397+ {[]any {[]any {1 , "one" }, "1abc" }, "oneabc" }, // []any with int pair (cast: 1→"1")
398+ // inline form
399+ {[]any {"a" , "b" , "aab" }, "bbb" },
400+ {[]any {"a" , "b" , "b" , "c" , "aab" }, "bbc" },
401+ {[]any {"app" , "pear" , "apple" , "orange" , "apple" }, "pearle" },
402+ {[]any {"a" , "b" , "" }, "" }, // empty source
403+ {[]any {template .HTML ("a" ), "b" , "aab" }, "bbb" }, // template.HTML pair
404+ {[]any {1 , "one" , "1abc" }, "oneabc" }, // int pair (cast: 1→"1")
405+ } {
406+ result , err := ns .ReplacePairs (test .args ... )
407+ c .Assert (err , qt .IsNil )
408+ c .Assert (result , qt .Equals , test .expect )
409+ }
410+
411+ for _ , test := range []struct {
412+ args []any
413+ errMatch string
414+ }{
415+ {[]any {}, "requires at least 2" }, // 0 args
416+ {[]any {"s" }, "requires at least 2" }, // 1 arg
417+ {[]any {42 , "s" }, "first must be a slice" }, // 2 args: non-slice first arg
418+ {[]any {"a" , "s" }, "first must be a slice" }, // 2 args: string first arg (not a slice)
419+ {[]any {[]string {"a" }, "s" }, "uneven number" }, // slice: odd pairs
420+ {[]any {"a" , "b" , "c" , "s" }, "uneven number" }, // inline: 3 pairs
421+ {[]any {[]any {tstNoStringer {}, "b" }, "s" }, "unable to cast" }, // non-castable slice element
422+ {[]any {tstNoStringer {}, "b" , "s" }, "unable to cast" }, // non-castable inline pair value
423+ {[]any {[]string {"a" , "b" }, tstNoStringer {}}, "unable to cast" }, // non-castable source
424+ } {
425+ _ , err := ns .ReplacePairs (test .args ... )
426+ c .Assert (err , qt .ErrorMatches , ".*" + test .errMatch + ".*" )
427+ }
428+ }
429+
430+ func BenchmarkReplacePairs (b * testing.B ) {
431+ twoPairs := []string {"a" , "A" , "b" , "B" }
432+ threePairs := []string {"a" , "A" , "b" , "B" , "c" , "C" }
433+ s := "aabbcc"
434+
435+ b .Run ("TwoPairs/cached" , func (b * testing.B ) {
436+ b .ReportAllocs ()
437+ for b .Loop () {
438+ ns .ReplacePairs (twoPairs , s )
439+ }
440+ })
441+
442+ b .Run ("TwoPairs/uncached" , func (b * testing.B ) {
443+ b .ReportAllocs ()
444+ for b .Loop () {
445+ stds .NewReplacer (twoPairs ... ).Replace (s )
446+ }
447+ })
448+
449+ b .Run ("ThreePairs/cached" , func (b * testing.B ) {
450+ b .ReportAllocs ()
451+ for b .Loop () {
452+ ns .ReplacePairs (threePairs , s )
453+ }
454+ })
455+
456+ b .Run ("ThreePairs/uncached" , func (b * testing.B ) {
457+ b .ReportAllocs ()
458+ for b .Loop () {
459+ stds .NewReplacer (threePairs ... ).Replace (s )
460+ }
461+ })
462+ }
463+
378464func TestSliceString (t * testing.T ) {
379465 t .Parallel ()
380466 c := qt .New (t )
0 commit comments