• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
6package str
7
8import (
9	"fmt"
10	"strings"
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.
17func StringList(args ...any) []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//
34//	strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
35//
36// This lets us test a large set of strings for fold-equivalent
37// duplicates without making a quadratic number of calls
38// to EqualFold. Note that strings.ToUpper and strings.ToLower
39// do not have the desired property in some corner cases.
40func ToFold(s string) string {
41	// Fast path: all ASCII, no upper case.
42	// Most paths look like this already.
43	for i := 0; i < len(s); i++ {
44		c := s[i]
45		if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
46			goto Slow
47		}
48	}
49	return s
50
51Slow:
52	var b strings.Builder
53	for _, r := range s {
54		// SimpleFold(x) cycles to the next equivalent rune > x
55		// or wraps around to smaller values. Iterate until it wraps,
56		// and we've found the minimum value.
57		for {
58			r0 := r
59			r = unicode.SimpleFold(r0)
60			if r <= r0 {
61				break
62			}
63		}
64		// Exception to allow fast path above: A-Z => a-z
65		if 'A' <= r && r <= 'Z' {
66			r += 'a' - 'A'
67		}
68		b.WriteRune(r)
69	}
70	return b.String()
71}
72
73// FoldDup reports a pair of strings from the list that are
74// equal according to strings.EqualFold.
75// It returns "", "" if there are no such strings.
76func FoldDup(list []string) (string, string) {
77	clash := map[string]string{}
78	for _, s := range list {
79		fold := ToFold(s)
80		if t := clash[fold]; t != "" {
81			if s > t {
82				s, t = t, s
83			}
84			return s, t
85		}
86		clash[fold] = s
87	}
88	return "", ""
89}
90
91// Uniq removes consecutive duplicate strings from ss.
92func Uniq(ss *[]string) {
93	if len(*ss) <= 1 {
94		return
95	}
96	uniq := (*ss)[:1]
97	for _, s := range *ss {
98		if s != uniq[len(uniq)-1] {
99			uniq = append(uniq, s)
100		}
101	}
102	*ss = uniq
103}
104