forked from kubernetes/kubernetes
/
acname.go
131 lines (114 loc) · 3.47 KB
/
acname.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
package types
import (
"encoding/json"
"errors"
"regexp"
"strings"
)
var (
// ValidACName is a regular expression that defines a valid ACName
ValidACName = regexp.MustCompile("^[a-z0-9]+([-./][a-z0-9]+)*$")
invalidChars = regexp.MustCompile("[^a-z0-9./-]")
invalidEdges = regexp.MustCompile("(^[./-]+)|([./-]+$)")
ErrEmptyACName = ACNameError("ACName cannot be empty")
ErrInvalidEdge = ACNameError("ACName must start and end with only lower case " +
"alphanumeric characters")
ErrInvalidChar = ACNameError("ACName must contain only lower case " +
`alphanumeric characters plus ".", "-", "/"`)
)
// ACName (an App-Container Name) is a format used by keys in different formats
// of the App Container Standard. An ACName is restricted to characters
// accepted by the DNS RFC[1] and "/"; all alphabetical characters must be
// lowercase only. Furthermore, the first and last character ("edges") must be
// alphanumeric, and an ACName cannot be empty. Programmatically, an ACName
// must conform to the regular expression ValidACName.
//
// [1] http://tools.ietf.org/html/rfc1123#page-13
type ACName string
func (n ACName) String() string {
return string(n)
}
// Set sets the ACName to the given value, if it is valid; if not,
// an error is returned.
func (n *ACName) Set(s string) error {
nn, err := NewACName(s)
if err == nil {
*n = *nn
}
return err
}
// Equals checks whether a given ACName is equal to this one.
func (n ACName) Equals(o ACName) bool {
return strings.ToLower(string(n)) == strings.ToLower(string(o))
}
// Empty returns a boolean indicating whether this ACName is empty.
func (n ACName) Empty() bool {
return n.String() == ""
}
// NewACName generates a new ACName from a string. If the given string is
// not a valid ACName, nil and an error are returned.
func NewACName(s string) (*ACName, error) {
n := ACName(s)
if err := n.assertValid(); err != nil {
return nil, err
}
return &n, nil
}
// MustACName generates a new ACName from a string, If the given string is
// not a valid ACName, it panics.
func MustACName(s string) *ACName {
n, err := NewACName(s)
if err != nil {
panic(err)
}
return n
}
func (n ACName) assertValid() error {
s := string(n)
if len(s) == 0 {
return ErrEmptyACName
}
if invalidChars.MatchString(s) {
return ErrInvalidChar
}
if invalidEdges.MatchString(s) {
return ErrInvalidEdge
}
return nil
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (n *ACName) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
nn, err := NewACName(s)
if err != nil {
return err
}
*n = *nn
return nil
}
// MarshalJSON implements the json.Marshaler interface
func (n ACName) MarshalJSON() ([]byte, error) {
if err := n.assertValid(); err != nil {
return nil, err
}
return json.Marshal(n.String())
}
// SanitizeACName replaces every invalid ACName character in s with a dash
// making it a legal ACName string. If the character is an upper case letter it
// replaces it with its lower case. It also removes illegal edge characters
// (hyphens, periods and slashes).
//
// This is a helper function and its algorithm is not part of the spec. It
// should not be called without the user explicitly asking for a suggestion.
func SanitizeACName(s string) (string, error) {
s = strings.ToLower(s)
s = invalidChars.ReplaceAllString(s, "-")
s = invalidEdges.ReplaceAllString(s, "")
if s == "" {
return "", errors.New("must contain at least one valid character")
}
return s, nil
}