forked from edhaight/atlas-app-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
v0.go
193 lines (158 loc) · 3.59 KB
/
v0.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
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
181
182
183
184
185
186
187
188
189
190
191
192
193
package bloxid
import (
"encoding/base32"
"errors"
"fmt"
"strings"
)
const (
VersionUnknown Version = iota
Version0 Version = iota
)
type Version uint8
func (v Version) String() string {
name, ok := version_name[v]
if !ok {
return version_name[VersionUnknown]
}
return name
}
var version_name = map[Version]string{
VersionUnknown: "unknown",
Version0: "blox0",
}
var name_version = map[string]Version{
"unknown": VersionUnknown,
"blox0": Version0,
}
var (
ErrInvalidVersion error = errors.New("invalid bloxid version")
ErrInvalidEntityType error = errors.New("entity type must be non-empty")
ErrInvalidUniqueIDLen error = errors.New("unique ID did not meet minimum length requirements")
ErrIDEmpty error = errors.New("empty bloxid")
ErrV0Parts error = errors.New("invalid number of parts found")
)
// NewV0 parse a string into a typed guid, return an error
// if the string fails validation.
func NewV0(bloxid string) (*V0, error) {
if len(bloxid) == 0 {
return nil, ErrIDEmpty
}
return parseV0(bloxid)
}
const V0Delimiter = "."
func parseV0(bloxid string) (*V0, error) {
if err := validateV0(bloxid); err != nil {
return nil, err
}
parts := strings.Split(bloxid, V0Delimiter)
v0 := &V0{
version: name_version[parts[0]],
entityType: parts[1],
region: parts[2],
encoded: parts[3],
}
decodedTall := strings.ToUpper(parts[3])
decoded, err := base32.StdEncoding.DecodeString(decodedTall)
if err != nil {
return nil, fmt.Errorf("unable to decode id: %s", err)
}
v0.decoded = fmt.Sprintf("%x", decoded)
return v0, nil
}
func validateV0(bloxid string) error {
parts := strings.Split(bloxid, V0Delimiter)
if len(parts) != 4 {
return ErrV0Parts
}
if ver := parts[0]; ver != Version0.String() {
return ErrInvalidVersion
}
if entityType := parts[1]; len(entityType) == 0 {
return ErrInvalidEntityType
}
if len(parts[3]) < DefaultEntropySize/2 {
return ErrInvalidUniqueIDLen
}
return nil
}
var _ ID = &V0{}
// V0 represents a typed guid
type V0 struct {
version Version
region string
customSuffix string
decoded string
encoded string
entityType string
shortID string
}
// Serialize the typed guid as a string
func (v *V0) String() string {
s := []string{
v.Version(),
v.entityType,
v.region,
v.encoded,
}
return strings.Join(s, ".")
}
// Region implements ID.Region
func (v *V0) Region() string {
if v == nil {
return ""
}
return v.region
}
// ShortID implements ID.ShortID
func (v *V0) ShortID() string {
if v == nil {
return ""
}
return v.shortID
}
// Type implements ID.Type
func (v *V0) Type() string {
if v == nil {
return ""
}
return v.entityType
}
// Version of the string
func (v *V0) Version() string {
if v == nil {
return VersionUnknown.String()
}
return v.version.String()
}
// V0Options required options to create a typed guid
type V0Options struct {
Region string
EntityType string
shortid string
}
type GenerateV0Opts func(o *V0Options)
func WithShortID(shortid string) func(o *V0Options) {
return func(o *V0Options) {
o.shortid = shortid
}
}
func GenerateV0(opts *V0Options, fnOpts ...GenerateV0Opts) (*V0, error) {
for _, fn := range fnOpts {
fn(opts)
}
encoded, decoded := uniqueID(opts)
return &V0{
version: Version0,
region: opts.Region,
decoded: decoded,
encoded: encoded,
entityType: opts.EntityType,
}, nil
}
func uniqueID(opts *V0Options) (encoded string, decoded string) {
entropy := randDefault()
decoded = fmt.Sprintf("%x", entropy)
encoded = strings.ToLower(base32.StdEncoding.EncodeToString(entropy))
return
}