eventreg.gno

7.72 Kb ยท 291 lines
  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}