-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathitem.go
More file actions
288 lines (272 loc) · 6.57 KB
/
item.go
File metadata and controls
288 lines (272 loc) · 6.57 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package activitypub
import (
"fmt"
"reflect"
"slices"
"strings"
)
// Item struct
type Item = ObjectOrLink
const (
// EmptyIRI represents a zero length IRI
EmptyIRI IRI = ""
// NilIRI represents by convention an IRI which is nil
// Its use is mostly to check if a property of an ActivityPub Item is nil
NilIRI IRI = "-"
// EmptyID represents a zero length ID
EmptyID = EmptyIRI
// NilID represents by convention an ID which is nil, see details of NilIRI
NilID = NilIRI
)
func itemsNeedSwapping(i1, i2 Item) bool {
if IsIRI(i1) && !IsIRI(i2) {
return true
}
t1 := i1.GetType()
t2 := i2.GetType()
if ObjectTypes.Match(t2) {
return !ObjectTypes.Match(t1)
}
return false
}
// ItemsEqual checks if it and with Items are equal
func ItemsEqual(it, with Item) bool {
if IsNil(it) || IsNil(with) {
return IsNil(with) && IsNil(it)
}
if itemsNeedSwapping(it, with) {
return ItemsEqual(with, it)
}
result := false
if IsIRI(with) || IsIRI(it) {
ii, ok := it.(IRI)
iw, wok := with.(IRI)
result = ok && wok && ii.Equal(iw)
} else if IsIRIs(it) {
if !IsIRIs(with) {
return false
}
iIRIs, _ := ToIRIs(it)
wIRIs, _ := ToIRIs(with)
return slices.Equal(*iIRIs, *wIRIs)
} else if IsItemCollection(it) {
if !IsItemCollection(with) {
return false
}
_ = OnItemCollection(it, func(c *ItemCollection) error {
result = c.Equals(with)
return nil
})
} else if IsLink(it) {
_ = OnLink(it, func(l *Link) error {
result = l.Equals(with)
return nil
})
} else if IsObject(it) {
_ = OnObject(it, func(i *Object) error {
result = i.Equals(with)
return nil
})
if ActivityTypes.Match(with.GetType()) {
_ = OnActivity(it, func(i *Activity) error {
result = i.Equals(with)
return nil
})
} else if ActorTypes.Match(with.GetType()) {
_ = OnActor(it, func(i *Actor) error {
result = i.Equals(with)
return nil
})
} else if it.IsCollection() {
if CollectionType.Match(it.GetType()) {
_ = OnCollection(it, func(c *Collection) error {
result = c.Equals(with)
return nil
})
}
if OrderedCollectionType.Match(it.GetType()) {
_ = OnOrderedCollection(it, func(c *OrderedCollection) error {
result = c.Equals(with)
return nil
})
}
if CollectionPageType.Match(it.GetType()) {
_ = OnCollectionPage(it, func(c *CollectionPage) error {
result = c.Equals(with)
return nil
})
}
if OrderedCollectionPageType.Match(it.GetType()) {
_ = OnOrderedCollectionPage(it, func(c *OrderedCollectionPage) error {
result = c.Equals(with)
return nil
})
}
}
}
return result
}
// IsItemCollection returns if the current Item interface holds a Collection
func IsItemCollection(it LinkOrIRI) bool {
if _, ok := it.(ItemCollection); ok {
return ok
}
if _, ok := it.(*ItemCollection); ok {
return ok
}
return !IsNil(it) && IsIRIs(it)
}
// IsIRI returns if the current Item interface holds an IRI
func IsIRI(it LinkOrIRI) bool {
if _, ok := it.(IRI); ok {
return true
}
if iri, ok := it.(*IRI); ok {
return iri != nil
}
return false
}
// IsIRIs returns if the current Item interface holds an IRI slice
func IsIRIs(it LinkOrIRI) bool {
_, ok := it.(IRIs)
_, okp := it.(*IRIs)
return ok || okp
}
// IsLink returns if the current Item interface holds a Link
func IsLink(it LinkOrIRI) bool {
if IsNil(it) {
return false
}
if _, ok := it.(Link); ok {
return true
}
if l, ok := it.(*Link); ok {
return l != nil
}
return false
}
// IsObject returns if the current Item interface holds an Object
func IsObject(it LinkOrIRI) bool {
if IsNil(it) {
return false
}
switch ob := it.(type) {
case Object:
return true
case *Object:
return ob != nil
case Actor:
return true
case *Actor:
return ob != nil
case Profile:
return true
case *Profile:
return ob != nil
case Place:
return true
case *Place:
return ob != nil
case Relationship:
return true
case *Relationship:
return ob != nil
case Tombstone:
return true
case *Tombstone:
return ob != nil
case Activity:
return true
case *Activity:
return ob != nil
case IntransitiveActivity:
return true
case *IntransitiveActivity:
return ob != nil
case Question:
return true
case *Question:
return ob != nil
case Collection:
return true
case *Collection:
return ob != nil
case CollectionPage:
return true
case *CollectionPage:
return ob != nil
case OrderedCollection:
return true
case *OrderedCollection:
return ob != nil
case OrderedCollectionPage:
return true
case *OrderedCollectionPage:
return ob != nil
default:
return false
}
}
// IsNil checks if the object matching an ObjectOrLink interface is nil
func IsNil(it LinkOrIRI) bool {
if it == nil {
return true
}
// This is the default if the argument can't be cast to Object, as is the case for an ItemCollection
switch maybeNil := it.(type) {
case IRI:
return len(maybeNil) == 0 || strings.EqualFold(maybeNil.String(), NilIRI.String())
case *IRI:
return maybeNil == nil || len(*maybeNil) == 0 || strings.EqualFold(maybeNil.String(), NilIRI.String())
case IRIs:
return maybeNil == nil
case *IRIs:
return maybeNil == nil
case ItemCollection:
return maybeNil == nil
case *ItemCollection:
return maybeNil == nil
}
// NOTE(marius): we're not dealing with a type that we know about, so we use slow reflection
// as we still care about the result
v := reflect.ValueOf(it)
return v.Kind() == reflect.Pointer && v.IsNil()
}
func ErrorInvalidType[T Objects | Links](received LinkOrIRI) error {
return fmt.Errorf("unable to convert %T to %T", received, new(T))
}
// OnItem runs function "fn" on the Item "it", with the benefit of destructuring "it" to individual
// items if it's actually an ItemCollection or an object holding an ItemCollection
//
// It is expected that the caller handles the logic of dealing with different Item implementations
// internally in "fn".
func OnItem(it Item, fn func(Item) error) error {
if IsNil(it) {
return nil
}
if !IsItemCollection(it) {
return fn(it)
}
return OnItemCollection(it, func(col *ItemCollection) error {
for _, it := range *col {
if err := OnItem(it, fn); err != nil {
return err
}
}
return nil
})
}
// OnItemCollection calls function fn on it Item if it can be asserted to type ItemCollection
//
// It should be used when Item represents an Item collection and it's usually used as a way
// to wrap functionality for other functions that will be called on each item in the collection.
func OnItemCollection(it LinkOrIRI, fn WithItemCollectionFn) error {
if IsNil(it) {
return nil
}
col, err := ToItemCollection(it)
if err != nil {
return err
}
return fn(col)
}