eventreg.gno
7.57 Kb ยท 280 lines
1package eventreg
2
3import (
4 "chain/runtime"
5 "errors"
6
7 "gno.land/p/nt/avl"
8 "gno.land/p/nt/ownable"
9 "gno.land/p/nt/seqid"
10 "gno.land/p/zenao/events"
11 zenaov1 "gno.land/p/zenao/zenao/v1"
12)
13
14// all dates are in unix seconds for easier interfacing with maketx call and vm/qeval
15
16var (
17 Ownable *ownable.Ownable
18
19 registered avl.Tree // <eventPkgPath> -> func() events.Info
20 eventsByPkgPath avl.Tree // <eventPkgPath> -> events.Info
21 eventsByEndDate avl.Tree // <endDateUnixSeconds>/<eventPkgPath> -> eventPkgPath
22 eventsByParticipant avl.Tree // <participantID>/<endDateUnixSeconds>/<eventPkgPath> -> eventPkgPath
23 eventsByOrganizer avl.Tree // <organizerID>/<endDateUnixSeconds>/<eventPkgPath> -> eventPkgPath
24 participantsByEvent avl.Tree // <eventPkgPath>/<participantID> -> participantID
25)
26
27func init() {
28 Ownable = ownable.NewWithAddress(address("g1djrkw9tf4px658j85cc6fhsvm50uf9s0g6kfsm")) // zenao-dev-admin
29}
30
31// XXX: split this package into eventreg and eventsindex
32
33func Register(_ realm, infoGetter events.InfoGetter) {
34 caller := runtime.PreviousRealm()
35 if caller.IsUser() {
36 panic("can't register user")
37 }
38 pkgPath := caller.PkgPath()
39
40 if infoGetter == nil {
41 registered.Remove(pkgPath)
42 // XXX: remove from index??
43 return
44 }
45 registered.Set(pkgPath, infoGetter)
46}
47
48func getInfo(pkgPath string) (*zenaov1.EventInfo, bool) {
49 raw, ok := registered.Get(pkgPath)
50 if !ok {
51 return nil, false
52 }
53 return raw.(events.InfoGetter)(), true
54}
55
56func mustGetInfo(pkgPath string) *zenaov1.EventInfo {
57 info, ok := getInfo(pkgPath)
58 if !ok {
59 panic(ErrEventNotFound)
60 }
61 return info
62}
63
64func IndexEvent(_ realm, pkgPath string) {
65 Ownable.AssertOwnedByPrevious()
66
67 if prev := getEventByPkgPath(pkgPath); prev != nil {
68 panic("already added")
69 }
70 info := mustGetInfo(pkgPath)
71 key := pkgPath
72 eventsByPkgPath.Set(key, info)
73
74 key = unixTimeKey(info.EndDate) + "/" + pkgPath
75 eventsByEndDate.Set(key, pkgPath)
76
77 for _, organizer := range info.Organizers {
78 key = organizer + "/" + unixTimeKey(info.EndDate) + "/" + pkgPath
79 eventsByOrganizer.Set(key, pkgPath)
80 }
81}
82
83func UpdateIndex(_ realm, pkgPath string) {
84 Ownable.AssertOwnedByPrevious()
85
86 prevInfo := mustGetEventByPkgPath(pkgPath)
87
88 info := mustGetInfo(pkgPath)
89 eventsByPkgPath.Set(pkgPath, info)
90
91 if prevInfo.EndDate != info.EndDate {
92 key := unixTimeKey(prevInfo.EndDate) + "/" + pkgPath
93 eventsByEndDate.Remove(key)
94
95 newKey := unixTimeKey(info.EndDate) + "/" + pkgPath
96 eventsByEndDate.Set(newKey, pkgPath)
97
98 for _, organizer := range prevInfo.Organizers {
99 key = organizer + "/" + unixTimeKey(prevInfo.EndDate) + "/" + pkgPath
100 eventsByOrganizer.Remove(key)
101 }
102
103 for _, organizer := range info.Organizers {
104 key = organizer + "/" + unixTimeKey(info.EndDate) + "/" + pkgPath
105 eventsByOrganizer.Set(key, pkgPath)
106 }
107
108 startKey := pkgPath + "/"
109 endKey := startKey[:len(startKey)-1] + string('/'+1)
110 participantsByEvent.Iterate(startKey, endKey, func(key string, value interface{}) bool {
111 userID := value.(string)
112
113 key = userID + "/" + unixTimeKey(prevInfo.EndDate) + "/" + pkgPath
114 eventsByParticipant.Remove(key)
115
116 newKey = userID + "/" + unixTimeKey(info.EndDate) + "/" + pkgPath
117 eventsByParticipant.Set(newKey, pkgPath)
118
119 return false
120 })
121 } else if !sliceIsEqual(prevInfo.Organizers, info.Organizers) {
122 for _, organizer := range prevInfo.Organizers {
123 key := organizer + "/" + unixTimeKey(prevInfo.EndDate) + "/" + pkgPath
124 eventsByOrganizer.Remove(key)
125 }
126 for _, organizer := range info.Organizers {
127 key := organizer + "/" + unixTimeKey(info.EndDate) + "/" + pkgPath
128 eventsByOrganizer.Set(key, pkgPath)
129 }
130 }
131}
132
133func RemoveIndex(_ realm, pkgPath string) {
134 Ownable.AssertOwnedByPrevious()
135
136 evt := mustGetEventByPkgPath(pkgPath)
137
138 eventsByPkgPath.Remove(pkgPath)
139
140 key := unixTimeKey(evt.EndDate) + "/" + pkgPath
141 eventsByEndDate.Remove(key)
142
143 for _, organizer := range evt.Organizers {
144 key = organizer + "/" + unixTimeKey(evt.EndDate) + "/" + pkgPath
145 eventsByOrganizer.Remove(key)
146 }
147
148 startKey := pkgPath + "/"
149 endKey := startKey[:len(startKey)-1] + string('/'+1)
150 participantsByEvent.Iterate(startKey, endKey, func(key string, value interface{}) bool {
151 userID := value.(string)
152
153 key = userID + "/" + unixTimeKey(evt.EndDate) + "/" + pkgPath
154 eventsByParticipant.Remove(key)
155
156 return false
157 })
158}
159
160func AddParticipant(_ realm, eventPkgPath string, userID string) {
161 Ownable.AssertOwnedByPrevious()
162
163 evt := mustGetEventByPkgPath(eventPkgPath)
164
165 key := userID + "/" + unixTimeKey(evt.EndDate) + "/" + eventPkgPath
166 eventsByParticipant.Set(key, eventPkgPath)
167
168 key = eventPkgPath + "/" + userID
169 participantsByEvent.Set(key, userID)
170
171 info := mustGetInfo(eventPkgPath)
172 eventsByPkgPath.Set(eventPkgPath, info)
173}
174
175func RemoveParticipant(_ realm, eventPkgPath string, userID string) {
176 Ownable.AssertOwnedByPrevious()
177
178 evt := mustGetEventByPkgPath(eventPkgPath)
179
180 key := userID + "/" + unixTimeKey(evt.EndDate) + "/" + eventPkgPath
181 eventsByParticipant.Remove(key)
182
183 key = eventPkgPath + "/" + userID
184 participantsByEvent.Remove(key)
185
186 info := mustGetInfo(eventPkgPath)
187 eventsByPkgPath.Set(eventPkgPath, info)
188}
189
190var ErrEventNotFound = errors.New("event not found")
191
192func getEventByPkgPath(pkgPath string) *zenaov1.EventInfo {
193 raw, ok := eventsByPkgPath.Get(pkgPath)
194 if !ok {
195 return nil
196 }
197 info := raw.(*zenaov1.EventInfo)
198 return info
199}
200
201func mustGetEventByPkgPath(pkgPath string) *zenaov1.EventInfo {
202 evt := getEventByPkgPath(pkgPath)
203 if evt == nil {
204 panic(ErrEventNotFound)
205 }
206 return evt
207}
208
209func listEvents(from, to int64, limit, offset uint32) []*zenaov1.EventInfo {
210 fromKey := unixTimeKey(from) + "/"
211 toKey := unixTimeKey(to) + "/"
212
213 return listEventsInternal(&eventsByEndDate, zenaov1.DISCOVERABLE_FILTER_DISCOVERABLE, fromKey, toKey, from > to, limit, offset)
214}
215
216func listEventsByOrganizer(organizerID string, discoverableFilter zenaov1.DiscoverableFilter, from, to int64, limit, offset uint32) []*zenaov1.EventInfo {
217 fromKey := organizerID + "/" + unixTimeKey(from) + "/"
218 toKey := organizerID + "/" + unixTimeKey(to) + "/"
219
220 return listEventsInternal(&eventsByOrganizer, discoverableFilter, fromKey, toKey, from > to, limit, offset)
221}
222
223func listEventsByParticipant(participantID string, from, to int64, limit, offset uint32) []*zenaov1.EventInfo {
224 fromKey := participantID + "/" + unixTimeKey(from) + "/"
225 toKey := participantID + "/" + unixTimeKey(to) + "/"
226
227 return listEventsInternal(&eventsByParticipant, zenaov1.DISCOVERABLE_FILTER_DISCOVERABLE, fromKey, toKey, from > to, limit, offset)
228}
229
230func listEventsInternal(at avl.ITree, discoverableFilter zenaov1.DiscoverableFilter, fromKey string, toKey string, rev bool, limit, offset uint32) []*zenaov1.EventInfo {
231 res := []*zenaov1.EventInfo{}
232 count := uint32(0)
233 it := func(key string, value interface{}) bool {
234 var evt zenaov1.EventInfo
235 switch val := value.(type) {
236 case *zenaov1.EventInfo:
237 evt = *val
238 evt.PkgPath = key
239 case string:
240 evt = *mustGetEventByPkgPath(val)
241 evt.PkgPath = val
242 }
243
244 if (!evt.Discoverable && discoverableFilter == zenaov1.DISCOVERABLE_FILTER_DISCOVERABLE) ||
245 (evt.Discoverable && discoverableFilter == zenaov1.DISCOVERABLE_FILTER_UNDISCOVERABLE) {
246 return false
247 }
248 if count < offset {
249 count++
250 return false
251 }
252 res = append(res, &evt)
253 return uint32(len(res)) >= limit
254 }
255 if rev {
256 at.ReverseIterate(toKey, fromKey, it)
257 } else {
258 at.Iterate(fromKey, toKey, it)
259 }
260 return res
261}
262
263func unixTimeKey(t int64) string {
264 if t < 0 {
265 panic("negative unix time")
266 }
267 return seqid.ID(t).Binary()
268}
269
270func sliceIsEqual(a, b []string) bool {
271 if len(a) != len(b) {
272 return false
273 }
274 for i, v := range a {
275 if v != b[i] {
276 return false
277 }
278 }
279 return true
280}