-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathtypes.go
155 lines (138 loc) · 3.63 KB
/
types.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
package res
import "encoding/json"
// Ref is a resource reference to another resource ID.
//
// It marshals into a reference object, eg.:
//
// {"rid":"userService.user.42"}
type Ref string
// SoftRef is a soft resource reference to another resource ID which will not
// automatically be followed by Resgate.
//
// It marshals into a soft reference object, eg.:
//
// {"rid":"userService.user.42","soft":true}
type SoftRef string
// DataValue is a wrapper for values that may marshal into any type of json
// value, including objects, arrays, or nested structures.
//
// If a value marshals into a json object or array, it must be wrapped with
// DataValue or similar, or else the value will be considered invalid.
//
// Example:
//
// s.Handle("timezones", res.GetCollection(func(r res.CollectionRequest) {
// type tz struct {
// Abbr string `json:"abbr"`
// Offset int `json:"offset"`
// }
// r.Collection([]res.DataValue{
// res.DataValue{tz{"GMT", 0}},
// res.DataValue{tz{"CET", 1}},
// })
// }))
//
// For objects and arrays, it marshals into a data value object, eg.:
//
// json.Marshal(res.DataValue{[]int{1, 2, 3}}) // Result: {"data":[1,2,3]}
//
// For strings, numbers, booleans, and null values, it marshals into a primitive value, eg.:
//
// json.Marshal(res.DataValue{nil}) // Result: null
type DataValue[T any] struct {
Data T `json:"data"`
}
// NewDataValue creates a new DataValue with the given data.
func NewDataValue[T any](data T) DataValue[T] { return DataValue[T]{Data: data} }
const (
refPrefix = `{"rid":`
softRefSuffix = `,"soft":true}`
refSuffix = '}'
)
// ResourceType enum
type ResourceType byte
// Resource type enum values
const (
TypeUnset ResourceType = iota
TypeModel
TypeCollection
)
// DeleteAction is used for deleted properties in "change" events
var DeleteAction = &struct{ json.RawMessage }{RawMessage: json.RawMessage(`{"action":"delete"}`)}
// MarshalJSON makes Ref implement the json.Marshaler interface.
func (r Ref) MarshalJSON() ([]byte, error) {
rid, err := json.Marshal(string(r))
if err != nil {
return nil, err
}
o := make([]byte, len(rid)+8)
copy(o, refPrefix)
copy(o[7:], rid)
o[len(o)-1] = refSuffix
return o, nil
}
// UnmarshalJSON makes Ref implement the json.Unmarshaler interface.
func (r *Ref) UnmarshalJSON(b []byte) error {
var p struct {
RID string `json:"rid"`
}
err := json.Unmarshal(b, &p)
if err != nil {
return err
}
*r = Ref(p.RID)
return nil
}
// IsValid returns true if the reference RID is valid, otherwise false.
func (r Ref) IsValid() bool {
return IsValidRID(string(r))
}
// MarshalJSON makes SoftRef implement the json.Marshaler interface.
func (r SoftRef) MarshalJSON() ([]byte, error) {
rid, err := json.Marshal(string(r))
if err != nil {
return nil, err
}
o := make([]byte, len(rid)+20)
copy(o, refPrefix)
copy(o[7:], rid)
copy(o[len(o)-13:], softRefSuffix)
return o, nil
}
// UnmarshalJSON makes SoftRef implement the json.Unmarshaler interface.
func (r *SoftRef) UnmarshalJSON(b []byte) error {
var p struct {
RID string `json:"rid"`
}
err := json.Unmarshal(b, &p)
if err != nil {
return err
}
*r = SoftRef(p.RID)
return nil
}
// IsValid returns true if the soft reference RID is valid, otherwise false.
func (r SoftRef) IsValid() bool {
return IsValidRID(string(r))
}
// IsValidRID returns true if the resource ID is valid, otherwise false.
func IsValidRID(rid string) bool {
start := true
for _, c := range rid {
if c == '?' {
return !start
}
if c < 33 || c > 126 || c == '*' || c == '>' {
return false
}
if c == '.' {
if start {
return false
}
start = true
} else {
start = false
}
}
return !start
}