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