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}