-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathitemId.go
More file actions
55 lines (47 loc) · 1.6 KB
/
itemId.go
File metadata and controls
55 lines (47 loc) · 1.6 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
package todo
import (
"errors"
"regexp"
)
// ItemId represents the ID of an item
// This type serves two purposes:
// - We want to avoid "prinitive obsession", that is, representing everything with primitive types
// - We want to ensure that not any string can be used as an ItemId. This is important, in a web app, for security.
// Since we will be building ItemId's using strings coming from outside the application, we want to frustrate any
// attempts to break the application by supplying inappropriate strings.
//
// The exported type is opaque; all you know is that it's something that can be represented as a string.
// The `implementsItemId` method is not exported, making it impossible for other packages to create an instance of this
// interface
type ItemId interface {
String() string
implementsItemId()
}
// The actual implementation of an ItemId is itemId, which is a wrapper type around string
type itemId string
// Satisfy the requirement to implement the exported interface ItemId
func (id itemId) implementsItemId() {
}
func (id itemId) String() string {
return string(id)
}
// A valid id is a nonempty sequence of digits
var validId = regexp.MustCompile("^\\d+$")
// NewItemId is the only way to create an ItemId
func NewItemId(s string) (ItemId, error) {
const maxLength = 10
if len(s) == 0 || len(s) > maxLength {
return nil, errors.New("invalid id length")
}
if !validId.MatchString(s) {
return nil, errors.New("invalid characters in id")
}
return itemId(s), nil
}
func MustNewItemId(s string) ItemId {
id, err := NewItemId(s)
if err != nil {
panic(err.Error())
}
return id
}