github.com/lingyao2333/mo-zero@v1.4.1/core/stringx/replacer.go (about)

     1  package stringx
     2  
     3  import "strings"
     4  
     5  type (
     6  	// Replacer interface wraps the Replace method.
     7  	Replacer interface {
     8  		Replace(text string) string
     9  	}
    10  
    11  	replacer struct {
    12  		*node
    13  		mapping map[string]string
    14  	}
    15  )
    16  
    17  // NewReplacer returns a Replacer.
    18  func NewReplacer(mapping map[string]string) Replacer {
    19  	rep := &replacer{
    20  		node:    new(node),
    21  		mapping: mapping,
    22  	}
    23  	for k := range mapping {
    24  		rep.add(k)
    25  	}
    26  	rep.build()
    27  
    28  	return rep
    29  }
    30  
    31  // Replace replaces text with given substitutes.
    32  func (r *replacer) Replace(text string) string {
    33  	var builder strings.Builder
    34  	var start int
    35  	chars := []rune(text)
    36  	size := len(chars)
    37  
    38  	for start < size {
    39  		cur := r.node
    40  
    41  		if start > 0 {
    42  			builder.WriteString(string(chars[:start]))
    43  		}
    44  
    45  		for i := start; i < size; i++ {
    46  			child, ok := cur.children[chars[i]]
    47  			if ok {
    48  				cur = child
    49  			} else if cur == r.node {
    50  				builder.WriteRune(chars[i])
    51  				// cur already points to root, set start only
    52  				start = i + 1
    53  				continue
    54  			} else {
    55  				curDepth := cur.depth
    56  				cur = cur.fail
    57  				child, ok = cur.children[chars[i]]
    58  				if !ok {
    59  					// write this path
    60  					builder.WriteString(string(chars[i-curDepth : i+1]))
    61  					// go to root
    62  					cur = r.node
    63  					start = i + 1
    64  					continue
    65  				}
    66  
    67  				failDepth := cur.depth
    68  				// write path before jump
    69  				builder.WriteString(string(chars[start : start+curDepth-failDepth]))
    70  				start += curDepth - failDepth
    71  				cur = child
    72  			}
    73  
    74  			if cur.end {
    75  				val := string(chars[i+1-cur.depth : i+1])
    76  				builder.WriteString(r.mapping[val])
    77  				builder.WriteString(string(chars[i+1:]))
    78  				// only matching this path, all previous paths are done
    79  				if start >= i+1-cur.depth && i+1 >= size {
    80  					return builder.String()
    81  				}
    82  
    83  				chars = []rune(builder.String())
    84  				size = len(chars)
    85  				builder.Reset()
    86  				break
    87  			}
    88  		}
    89  
    90  		if !cur.end {
    91  			builder.WriteString(string(chars[start:]))
    92  			return builder.String()
    93  		}
    94  	}
    95  
    96  	return string(chars)
    97  }