-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathprefix.go
131 lines (117 loc) · 3.13 KB
/
prefix.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 (
"database/sql/driver"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"github.com/kevinburke/go.uuid"
"gopkg.in/mgo.v2/bson"
)
// A PrefixUUID stores an additional prefix as part of a UUID type.
type PrefixUUID struct {
Prefix string
UUID uuid.UUID
}
// NilUUID is the empty PrefixUUID.
var NilUUID = PrefixUUID{UUID: uuid.Nil}
func (u PrefixUUID) String() string {
return u.Prefix + u.UUID.String()
}
// GenerateUUID generates a UUID with the given prefix.
func GenerateUUID(prefix string) PrefixUUID {
return PrefixUUID{
Prefix: prefix,
UUID: uuid.NewV4(),
}
}
// NewPrefixUUID creates a PrefixUUID from the prefix and string uuid. Returns
// an error if uuidstr cannot be parsed as a valid UUID.
func NewPrefixUUID(caboodle string) (PrefixUUID, error) {
if len(caboodle) < 36 {
return PrefixUUID{}, fmt.Errorf("types: Could not parse \"%s\" as a UUID with a prefix", caboodle)
}
uuidPart := caboodle[len(caboodle)-36:]
u, err := uuid.FromString(uuidPart)
if err != nil {
return PrefixUUID{}, err
}
return PrefixUUID{
Prefix: caboodle[:len(caboodle)-36],
UUID: u,
}, nil
}
func (pu *PrefixUUID) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
p, err := NewPrefixUUID(s)
if err != nil {
return err
}
*pu = p
return nil
}
func (pu PrefixUUID) MarshalJSON() ([]byte, error) {
return json.Marshal(pu.String())
}
// Scan implements the Scanner interface. Note only the UUID gets scanned/set
// here, we can't determine the prefix from the database. `value` should be
// a [16]byte or a string.
func (pu *PrefixUUID) Scan(value interface{}) error {
if value == nil {
return errors.New("types: cannot scan null into a PrefixUUID")
}
var err error
switch t := value.(type) {
case []byte:
if len(t) >= 32 {
*pu, err = NewPrefixUUID(string(t))
} else {
var u uuid.UUID
u, err = uuid.FromBytes(t)
pu.UUID = u
}
case string:
*pu, err = NewPrefixUUID(t)
default:
return fmt.Errorf("types: can't scan value of unknown type %v into a PrefixUUID", value)
}
return err
}
// Value implements the driver.Valuer interface.
func (pu PrefixUUID) Value() (driver.Value, error) {
// In theory we should be able to send 16 raw bytes to the database
// and have it encoded as a UUID. However, this requires enabling
// binary_parameters=yes on the connection string. Instead of that, just
// pass a string to the database, which is easy to handle.
return pu.UUID.String(), nil
}
// GetBSON implements the mgo.Getter interface.
func (pu PrefixUUID) GetBSON() (interface{}, error) {
return bson.Binary{
Kind: 0x03,
Data: pu.UUID[:],
}, nil
}
// SetBSON implements the mgo.Setter interface.
func (pu *PrefixUUID) SetBSON(raw bson.Raw) error {
// first 4 bytes are int32 LE length
if len(raw.Data) < 4 {
return fmt.Errorf("invalid BSON data: too short")
}
l := binary.LittleEndian.Uint32(raw.Data[:4])
var err error
if l >= 32 {
// null terminated, so subtract 1
d := string(raw.Data[4 : len(raw.Data)-1])
*pu, err = NewPrefixUUID(d)
} else {
var u uuid.UUID
u, err = uuid.FromBytes(raw.Data[4:])
pu.UUID = u
}
return err
}