forked from maddyblue/sqlfmt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
strings.go
executable file
·140 lines (126 loc) · 3.4 KB
/
strings.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2016 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package util
import (
"bytes"
"fmt"
io "io"
"strings"
"unicode/utf8"
"github.com/cockroachdb/errors"
)
// GetSingleRune decodes the string s as a single rune if possible.
func GetSingleRune(s string) (rune, error) {
if s == "" {
return 0, nil
}
r, sz := utf8.DecodeRuneInString(s)
if r == utf8.RuneError {
return 0, errors.Errorf("invalid character: %s", s)
}
if sz != len(s) {
return r, errors.New("must be only one character")
}
return r, nil
}
// ToLowerSingleByte returns the lowercase of a given single ASCII byte.
// A non ASCII byte is returned unchanged.
func ToLowerSingleByte(b byte) byte {
if b >= 'A' && b <= 'Z' {
return 'a' + (b - 'A')
}
return b
}
// TruncateString truncates a string to a given number of runes.
func TruncateString(s string, maxRunes int) string {
// This is a fast path (len(s) is an upper bound for RuneCountInString).
if len(s) <= maxRunes {
return s
}
n := utf8.RuneCountInString(s)
if n <= maxRunes {
return s
}
// Fast path for ASCII strings.
if len(s) == n {
return s[:maxRunes]
}
i := 0
for pos := range s {
if i == maxRunes {
return s[:pos]
}
i++
}
// This code should be unreachable.
return s
}
// RemoveTrailingSpaces splits the input string into lines, trims any trailing
// spaces from each line, then puts the lines back together.
//
// Any newlines at the end of the input string are ignored.
//
// The output string always ends in a newline.
func RemoveTrailingSpaces(input string) string {
lines := strings.TrimRight(input, "\n")
var buf bytes.Buffer
for _, line := range strings.Split(lines, "\n") {
fmt.Fprintf(&buf, "%s\n", strings.TrimRight(line, " "))
}
return buf.String()
}
// StringListBuilder helps printing out lists of items. See
// MakeStringListBuilder.
type StringListBuilder struct {
begin, separator, end string
// started is true if we had at least one entry (and thus wrote out <begin>).
started bool
}
// MakeStringListBuilder creates a StringListBuilder, which is used to print out
// lists of items. Sample usage:
//
// b := MakeStringListBuilder("(", ", ", ")")
// b.Add(&buf, "x")
// b.Add(&buf, "y")
// b.Finish(&buf) // By now, we wrote "(x, y)".
//
// If Add is not called, nothing is written.
func MakeStringListBuilder(begin, separator, end string) StringListBuilder {
return StringListBuilder{
begin: begin,
separator: separator,
end: end,
started: false,
}
}
func (b *StringListBuilder) prepareToAdd(w io.Writer) {
if b.started {
_, _ = w.Write([]byte(b.separator))
} else {
_, _ = w.Write([]byte(b.begin))
b.started = true
}
}
// Add an item to the list.
func (b *StringListBuilder) Add(w io.Writer, val string) {
b.prepareToAdd(w)
_, _ = w.Write([]byte(val))
}
// Addf is a format variant of Add.
func (b *StringListBuilder) Addf(w io.Writer, format string, args ...interface{}) {
b.prepareToAdd(w)
fmt.Fprintf(w, format, args...)
}
// Finish must be called after all the elements have been added.
func (b *StringListBuilder) Finish(w io.Writer) {
if b.started {
_, _ = w.Write([]byte(b.end))
}
}