communityreg.gno
4.46 Kb ยท 167 lines
1package communityreg
2
3import (
4 "chain/runtime"
5 "errors"
6
7 "gno.land/p/nt/avl"
8 "gno.land/p/nt/ownable"
9 "gno.land/p/zenao/communities"
10 zenaov1 "gno.land/p/zenao/zenao/v1"
11)
12
13var (
14 Ownable *ownable.Ownable
15
16 registered avl.Tree // <communityPkgPath> -> func() communities.Info
17 communityesByMembers avl.Tree // <memberID>/<communityPkgPath> -> communityPkgPath
18 communitiesByPkgPath avl.Tree // <communityPkgPath> -> communities.Info
19 communitiesByEvent avl.Tree // <eventPkgPath>/<communityID> -> communityID
20
21 ErrCommunityNotFound = errors.New("community not found")
22)
23
24func init() {
25 Ownable = ownable.NewWithAddress(address("g1djrkw9tf4px658j85cc6fhsvm50uf9s0g6kfsm")) // zenao-dev-admin
26}
27
28// XXX: split this package into communityreg and communitiesindex
29
30func Register(_ realm, infoGetter communities.InfoGetter) {
31 caller := runtime.PreviousRealm()
32 if caller.IsUser() {
33 panic("can't register user")
34 }
35 pkgPath := caller.PkgPath()
36
37 if infoGetter == nil {
38 registered.Remove(pkgPath)
39 // XXX: remove from index??
40 return
41 }
42
43 registered.Set(pkgPath, infoGetter)
44}
45
46func getInfo(pkgPath string) (*zenaov1.CommunityInfo, bool) {
47 raw, ok := registered.Get(pkgPath)
48 if !ok {
49 return nil, false
50 }
51 return raw.(communities.InfoGetter)(), true
52}
53
54func mustGetInfo(pkgPath string) *zenaov1.CommunityInfo {
55 info, ok := getInfo(pkgPath)
56 if !ok {
57 panic(ErrCommunityNotFound)
58 }
59 return info
60}
61
62func IndexCommunity(_ realm, pkgPath string) {
63 Ownable.AssertOwnedByPrevious()
64
65 if exist := getCommunityByPkgPath(pkgPath); exist != nil {
66 panic("community already indexed: " + pkgPath)
67 }
68
69 info := mustGetInfo(pkgPath)
70 communitiesByPkgPath.Set(pkgPath, info)
71}
72
73func AddMember(_ realm, communityPkgPath string, memberID string) {
74 Ownable.AssertOwnedByPrevious()
75 mustGetCommunityByPkgPath(communityPkgPath)
76
77 key := memberID + "/" + communityPkgPath
78 communityesByMembers.Set(key, communityPkgPath)
79 info := mustGetInfo(communityPkgPath)
80 communitiesByPkgPath.Set(communityPkgPath, info)
81}
82
83func RemoveMember(_ realm, communityPkgPath string, memberID string) {
84 Ownable.AssertOwnedByPrevious()
85 mustGetCommunityByPkgPath(communityPkgPath)
86
87 key := memberID + "/" + communityPkgPath
88 communityesByMembers.Remove(key)
89 info := mustGetInfo(communityPkgPath)
90 communitiesByPkgPath.Set(communityPkgPath, info)
91}
92
93func AddEvent(_ realm, communityPkgPath string, eventID string) {
94 Ownable.AssertOwnedByPrevious()
95 mustGetCommunityByPkgPath(communityPkgPath)
96 key := eventID + "/" + communityPkgPath
97 communitiesByEvent.Set(key, communityPkgPath)
98}
99
100func RemoveEvent(_ realm, communityPkgPath string, eventID string) {
101 Ownable.AssertOwnedByPrevious()
102 mustGetCommunityByPkgPath(communityPkgPath)
103 key := eventID + "/" + communityPkgPath
104 communitiesByEvent.Remove(key)
105}
106
107func getCommunityByPkgPath(pkgPath string) *zenaov1.CommunityInfo {
108 raw, ok := communitiesByPkgPath.Get(pkgPath)
109 if !ok {
110 return nil
111 }
112 return raw.(*zenaov1.CommunityInfo)
113}
114
115func mustGetCommunityByPkgPath(pkgPath string) *zenaov1.CommunityInfo {
116 info := getCommunityByPkgPath(pkgPath)
117 if info == nil {
118 panic(ErrCommunityNotFound)
119 }
120 return info
121}
122
123func listCommunities(limit, offset uint32) []*zenaov1.CommunityInfo {
124 return listCommunitiesInternal(&communitiesByPkgPath, "", "", false, limit, offset)
125}
126
127func listCommunitiesByMembers(memberID string, limit, offset uint32) []*zenaov1.CommunityInfo {
128 // ff is 255 in hex, which is the highest byte value, so it will match all keys that start with memberID.
129 fromKey := memberID + "/"
130 toKey := memberID + "/\xff"
131
132 return listCommunitiesInternal(&communityesByMembers, fromKey, toKey, false, limit, offset)
133}
134
135func listCommunitiesByEvent(eventID string, limit, offset uint32) []*zenaov1.CommunityInfo {
136 fromKey := eventID + "/"
137 toKey := eventID + "/\xff"
138
139 return listCommunitiesInternal(&communitiesByEvent, fromKey, toKey, false, limit, offset)
140}
141
142func listCommunitiesInternal(at avl.ITree, fromKey string, toKey string, rev bool, limit, offset uint32) []*zenaov1.CommunityInfo {
143 res := []*zenaov1.CommunityInfo{}
144 count := uint32(0)
145 it := func(key string, value interface{}) bool {
146 if count < offset {
147 count++
148 return false
149 }
150 var info zenaov1.CommunityInfo
151 switch val := value.(type) {
152 case *zenaov1.CommunityInfo:
153 info = *val
154 case string:
155 info = *mustGetCommunityByPkgPath(val)
156 }
157 info.PkgPath = key
158 res = append(res, &info)
159 return uint32(len(res)) >= limit
160 }
161 if rev {
162 at.ReverseIterate(toKey, fromKey, it)
163 } else {
164 at.Iterate(fromKey, toKey, it)
165 }
166 return res
167}