-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmust.go
139 lines (115 loc) · 3.47 KB
/
must.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
// Copyright Josh Komoroske. All rights reserved.
// Use of this source code is governed by the MIT license,
// a copy of which can be found in the LICENSE.txt file.
package meta
import (
"fmt"
"net/mail"
u "net/url"
"regexp"
"strconv"
"strings"
"time"
)
// mustAuthor validates that the given value contains the author's name and
// potentially email.
func mustAuthor(_, raw string) (string, string) {
if raw == "" {
return "", ""
}
parsed, err := mail.ParseAddress(raw)
if err != nil {
return raw, ""
}
return parsed.Name, parsed.Address
}
// mustBool validates that the given value is a properly formatted boolean.
func mustBool(_, raw string) bool {
if raw == "" {
return false
}
if b, err := strconv.ParseBool(raw); err == nil {
return b
}
return false
}
// semverRegex is the suggested regex for matching valid semver versions.
// See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string.
var semverRegex = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) //nolint:lll
// mustSemver validates that the given value is a properly formatted semver version.
func mustSemver(_, raw string) (string, string, string, string, string) {
matches := semverRegex.FindStringSubmatch(strings.TrimPrefix(raw, "v"))
switch len(matches) {
case 6: //nolint:gomnd
return matches[1], matches[2], matches[3], matches[4], matches[5]
case 5: //nolint:gomnd
return matches[1], matches[2], matches[3], matches[4], ""
case 4: //nolint:gomnd
return matches[1], matches[2], matches[3], "", ""
default:
return "", "", "", "", ""
}
}
// mustSHA validates that the given value is a properly formatted git SHA.
func mustSHA(path, raw string) string {
if raw == "" {
return ""
}
// Git SHAs are 40 characters long.
const gitSHALength = 40
if len(raw) != gitSHALength {
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
// Git SHAs are made of only lowercase hex characters.
for _, rune := range raw {
switch {
case '0' <= rune && rune <= '9':
case 'a' <= rune && rune <= 'f':
default:
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
}
return raw
}
// mustTime validates that the given value is a properly formatted timestamp.
// All timestamps are converted to UTC.
func mustTime(path, raw string) *time.Time {
if raw == "" {
return nil
}
layouts := []string{
time.RFC1123Z,
time.RFC3339,
// The format sometimes produced by `date --iso-8601=seconds`.
// See https://github.com/golang/go/issues/31113#issuecomment-482158617.
"2006-01-02T15:04:05Z0700",
}
// Try each layout until one parses.
for _, spec := range layouts {
if t, err := time.Parse(spec, raw); err == nil {
t = t.UTC()
return &t
}
}
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
// mustURL validates that the given value is a properly formatted URL.
func mustURL(path, raw string) *u.URL {
if raw == "" {
return nil
}
// Parse the URL.
parsed, err := u.Parse(raw)
if err != nil {
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
// Require that the scheme is http:// or https://.
if parsed.Scheme != "http" && parsed.Scheme != "https" {
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
// Require that the URL contained a host.
if parsed.Host == "" {
panic(fmt.Errorf("malformed ldflags value for %s", path))
}
return parsed
}