-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathidentifiable.go
More file actions
180 lines (152 loc) · 5.11 KB
/
identifiable.go
File metadata and controls
180 lines (152 loc) · 5.11 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package component // import "go.opentelemetry.io/collector/component"
import (
"encoding"
"errors"
"fmt"
"regexp"
"strings"
)
// typeAndNameSeparator is the separator that is used between type and name in type/name composite keys.
const typeAndNameSeparator = "/"
var (
// typeRegexp is used to validate the type of component.
// A type must start with an ASCII alphabetic character and
// can only contain ASCII alphanumeric characters and '_'.
// This must be kept in sync with the regex in cmd/mdatagen/validate.go.
typeRegexp = regexp.MustCompile(`^[a-zA-Z][0-9a-zA-Z_]{0,62}$`)
// nameRegexp is used to validate the name of a component. A name can consist of
// 1 to 1024 Unicode characters excluding whitespace, control characters, and
// symbols.
nameRegexp = regexp.MustCompile(`^[^\pZ\pC\pS]+$`)
)
var _ fmt.Stringer = Type{}
// Type is the component type as it is used in the config.
type Type struct {
name string
}
// String returns the string representation of the type.
func (t Type) String() string {
return t.name
}
// MarshalText marshals returns the Type name.
func (t Type) MarshalText() ([]byte, error) {
return []byte(t.name), nil
}
// NewType creates a type. It returns an error if the type is invalid.
// A type must
// - have at least one character,
// - start with an ASCII alphabetic character and
// - can only contain ASCII alphanumeric characters and '_'.
func NewType(ty string) (Type, error) {
if ty == "" {
return Type{}, errors.New("id must not be empty")
}
if !typeRegexp.MatchString(ty) {
return Type{}, fmt.Errorf("invalid character(s) in type %q", ty)
}
return Type{name: ty}, nil
}
// MustNewType creates a type. It panics if the type is invalid.
// A type must
// - have at least one character,
// - start with an ASCII alphabetic character and
// - can only contain ASCII alphanumeric characters and '_'.
func MustNewType(strType string) Type {
ty, err := NewType(strType)
if err != nil {
panic(err)
}
return ty
}
var (
_ fmt.Stringer = ID{}
_ encoding.TextMarshaler = ID{}
_ encoding.TextUnmarshaler = (*ID)(nil)
)
// ID represents the identity for a component. It combines two values:
// * type - the Type of the component.
// * name - the name of that component.
// The component ID (combination type + name) is unique for a given component.Kind.
type ID struct {
typeVal Type `mapstructure:"-"`
nameVal string `mapstructure:"-"`
}
// NewID returns a new ID with the given Type and empty name.
func NewID(typeVal Type) ID {
return ID{typeVal: typeVal}
}
// MustNewID builds a Type and returns a new ID with the given Type and empty name.
// This is equivalent to NewID(MustNewType(typeVal)).
// See MustNewType to check the valid values of typeVal.
func MustNewID(typeVal string) ID {
return NewID(MustNewType(typeVal))
}
// NewIDWithName returns a new ID with the given Type and name.
func NewIDWithName(typeVal Type, nameVal string) ID {
return ID{typeVal: typeVal, nameVal: nameVal}
}
// MustNewIDWithName builds a Type and returns a new ID with the given Type and name.
// This is equivalent to NewIDWithName(MustNewType(typeVal), nameVal).
// See MustNewType to check the valid values of typeVal.
func MustNewIDWithName(typeVal, nameVal string) ID {
return NewIDWithName(MustNewType(typeVal), nameVal)
}
// Type returns the type of the component.
func (id ID) Type() Type {
return id.typeVal
}
// Name returns the custom name of the component.
func (id ID) Name() string {
return id.nameVal
}
// MarshalText implements the encoding.TextMarshaler interface.
// This marshals the type and name as one string in the config.
func (id ID) MarshalText() ([]byte, error) {
return []byte(id.String()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (id *ID) UnmarshalText(text []byte) error {
idStr := string(text)
typeStr, nameStr, hasName := strings.Cut(idStr, typeAndNameSeparator)
typeStr = strings.TrimSpace(typeStr)
if typeStr == "" {
if hasName {
return fmt.Errorf("in %q id: the part before %s should not be empty", idStr, typeAndNameSeparator)
}
return errors.New("id must not be empty")
}
if hasName {
// "name" part is present.
nameStr = strings.TrimSpace(nameStr)
if nameStr == "" {
return fmt.Errorf("in %q id: the part after %s should not be empty", idStr, typeAndNameSeparator)
}
if err := validateName(nameStr); err != nil {
return fmt.Errorf("in %q id: %w", nameStr, err)
}
}
var err error
if id.typeVal, err = NewType(typeStr); err != nil {
return fmt.Errorf("in %q id: %w", idStr, err)
}
id.nameVal = nameStr
return nil
}
// String returns the ID string representation as "type[/name]" format.
func (id ID) String() string {
if id.nameVal == "" {
return id.typeVal.String()
}
return id.typeVal.String() + typeAndNameSeparator + id.nameVal
}
func validateName(nameStr string) error {
if len(nameStr) > 1024 {
return fmt.Errorf("name %q is longer than 1024 characters (%d characters)", nameStr, len(nameStr))
}
if !nameRegexp.MatchString(nameStr) {
return fmt.Errorf("invalid character(s) in name %q", nameStr)
}
return nil
}