Home | History | Annotate | Download | only in str
      1 // Copyright 2017 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package str provides string manipulation utilities.
      6 package str
      7 
      8 import (
      9 	"bytes"
     10 	"fmt"
     11 	"unicode"
     12 	"unicode/utf8"
     13 )
     14 
     15 // StringList flattens its arguments into a single []string.
     16 // Each argument in args must have type string or []string.
     17 func StringList(args ...interface{}) []string {
     18 	var x []string
     19 	for _, arg := range args {
     20 		switch arg := arg.(type) {
     21 		case []string:
     22 			x = append(x, arg...)
     23 		case string:
     24 			x = append(x, arg)
     25 		default:
     26 			panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
     27 		}
     28 	}
     29 	return x
     30 }
     31 
     32 // ToFold returns a string with the property that
     33 //	strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
     34 // This lets us test a large set of strings for fold-equivalent
     35 // duplicates without making a quadratic number of calls
     36 // to EqualFold. Note that strings.ToUpper and strings.ToLower
     37 // do not have the desired property in some corner cases.
     38 func ToFold(s string) string {
     39 	// Fast path: all ASCII, no upper case.
     40 	// Most paths look like this already.
     41 	for i := 0; i < len(s); i++ {
     42 		c := s[i]
     43 		if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
     44 			goto Slow
     45 		}
     46 	}
     47 	return s
     48 
     49 Slow:
     50 	var buf bytes.Buffer
     51 	for _, r := range s {
     52 		// SimpleFold(x) cycles to the next equivalent rune > x
     53 		// or wraps around to smaller values. Iterate until it wraps,
     54 		// and we've found the minimum value.
     55 		for {
     56 			r0 := r
     57 			r = unicode.SimpleFold(r0)
     58 			if r <= r0 {
     59 				break
     60 			}
     61 		}
     62 		// Exception to allow fast path above: A-Z => a-z
     63 		if 'A' <= r && r <= 'Z' {
     64 			r += 'a' - 'A'
     65 		}
     66 		buf.WriteRune(r)
     67 	}
     68 	return buf.String()
     69 }
     70 
     71 // FoldDup reports a pair of strings from the list that are
     72 // equal according to strings.EqualFold.
     73 // It returns "", "" if there are no such strings.
     74 func FoldDup(list []string) (string, string) {
     75 	clash := map[string]string{}
     76 	for _, s := range list {
     77 		fold := ToFold(s)
     78 		if t := clash[fold]; t != "" {
     79 			if s > t {
     80 				s, t = t, s
     81 			}
     82 			return s, t
     83 		}
     84 		clash[fold] = s
     85 	}
     86 	return "", ""
     87 }
     88 
     89 // Contains reports whether x contains s.
     90 func Contains(x []string, s string) bool {
     91 	for _, t := range x {
     92 		if t == s {
     93 			return true
     94 		}
     95 	}
     96 	return false
     97 }
     98 
     99 func isSpaceByte(c byte) bool {
    100 	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
    101 }
    102 
    103 // SplitQuotedFields splits s into a list of fields,
    104 // allowing single or double quotes around elements.
    105 // There is no unescaping or other processing within
    106 // quoted fields.
    107 func SplitQuotedFields(s string) ([]string, error) {
    108 	// Split fields allowing '' or "" around elements.
    109 	// Quotes further inside the string do not count.
    110 	var f []string
    111 	for len(s) > 0 {
    112 		for len(s) > 0 && isSpaceByte(s[0]) {
    113 			s = s[1:]
    114 		}
    115 		if len(s) == 0 {
    116 			break
    117 		}
    118 		// Accepted quoted string. No unescaping inside.
    119 		if s[0] == '"' || s[0] == '\'' {
    120 			quote := s[0]
    121 			s = s[1:]
    122 			i := 0
    123 			for i < len(s) && s[i] != quote {
    124 				i++
    125 			}
    126 			if i >= len(s) {
    127 				return nil, fmt.Errorf("unterminated %c string", quote)
    128 			}
    129 			f = append(f, s[:i])
    130 			s = s[i+1:]
    131 			continue
    132 		}
    133 		i := 0
    134 		for i < len(s) && !isSpaceByte(s[i]) {
    135 			i++
    136 		}
    137 		f = append(f, s[:i])
    138 		s = s[i:]
    139 	}
    140 	return f, nil
    141 }
    142