user.gno
5.81 Kb ยท 275 lines
1package user
2
3import (
4 "chain"
5 "chain/runtime"
6 "strings"
7
8 "gno.land/p/nt/avl"
9)
10
11// Events
12const (
13 CreateUserEvent = "CreateUser"
14 DeleteUserEvent = "DeleteUser"
15 SetPropertyEvent = "SetProperty"
16 SetPropertiesEvent = "SetProperties"
17 SetMetadatumEvent = "SetMetadatum"
18 SetMetadataEvent = "SetMetadata"
19)
20
21// properties: admin-managed user attributes
22var properties = avl.NewTree() // address => map[string]string
23
24// metadatas: user-managed personal data
25var metadatas = avl.NewTree() // address => map[string]string
26
27func HasUser(addr address) bool {
28 _, found := properties.Get(addr.String())
29 return found
30}
31
32func CreateUser(cur realm, addr address, metadata string) {
33 caller := runtime.PreviousRealm().Address()
34 if caller != getAdmin() && caller != addr {
35 panic("caller must be admin or the user being created")
36 }
37
38 addrStr := addr.String()
39 _, found := properties.Get(addrStr)
40 if found {
41 panic("user already exists: " + addrStr)
42 }
43
44 // Create properties node with address
45 properties.Set(addrStr, map[string]string{"address": addrStr})
46
47 // Parse and set metadata
48 metas := map[string]string{"address": addrStr}
49 if metadata != "" {
50 keyValuePairs := strings.Split(metadata, "|")
51 if len(keyValuePairs)%2 != 0 {
52 panic("metadata must be key|value pairs")
53 }
54 for i := 0; i < len(keyValuePairs); i += 2 {
55 key := keyValuePairs[i]
56 value := keyValuePairs[i+1]
57 if key == "address" {
58 panic("reserved key: address")
59 }
60 metas[key] = value
61 }
62 }
63 metadatas.Set(addrStr, metas)
64
65 chain.Emit(CreateUserEvent,
66 "address", addrStr,
67 "caller", caller.String(),
68 )
69}
70
71func DeleteUser(cur realm, addr address) {
72 caller := runtime.PreviousRealm().Address()
73 if caller != getAdmin() && caller != addr {
74 panic("caller must be admin or the user being deleted")
75 }
76
77 addrStr := addr.String()
78 _, found := properties.Get(addrStr)
79 if !found {
80 panic("user not found: " + addrStr)
81 }
82
83 properties.Remove(addrStr)
84 metadatas.Remove(addrStr)
85
86 chain.Emit(DeleteUserEvent,
87 "address", addrStr,
88 "caller", caller.String(),
89 )
90}
91
92func SetProperty(cur realm, addr address, key string, value string) {
93 caller := runtime.PreviousRealm().Address()
94 assertIsAdmin(caller)
95
96 if key == "address" {
97 panic("reserved key: " + key)
98 }
99
100 addrStr := addr.String()
101 props, found := properties.Get(addrStr)
102 if !found {
103 panic("user not found: " + addrStr)
104 }
105
106 props.(map[string]string)[key] = value
107
108 chain.Emit(
109 SetPropertyEvent,
110 "address", addrStr,
111 "key", key,
112 "value", value,
113 "caller", caller.String(),
114 )
115}
116
117func SetProperties(cur realm, addr address, keyValues string) {
118 caller := runtime.PreviousRealm().Address()
119 assertIsAdmin(caller)
120
121 keyValuePairs := strings.Split(keyValues, "|")
122 if len(keyValuePairs)%2 != 0 {
123 panic("keyValues must be key|value pairs")
124 }
125
126 addrStr := addr.String()
127 props, found := properties.Get(addrStr)
128 if !found {
129 panic("user not found: " + addrStr)
130 }
131
132 propsMap := props.(map[string]string)
133 for i := 0; i < len(keyValuePairs); i += 2 {
134 key := keyValuePairs[i]
135 if key == "address" {
136 continue
137 }
138 value := keyValuePairs[i+1]
139 propsMap[key] = value
140 }
141
142 chain.Emit(
143 SetPropertiesEvent,
144 "address", addrStr,
145 "caller", caller.String(),
146 )
147}
148
149func SetMetadatum(cur realm, addr address, key string, value string) {
150 caller := runtime.PreviousRealm().Address()
151 if caller != getAdmin() && caller != addr {
152 panic("caller must be admin or the user owner")
153 }
154
155 addrStr := addr.String()
156 metas, found := metadatas.Get(addrStr)
157 if !found {
158 panic("user not found: " + addrStr)
159 }
160
161 if key == "address" {
162 panic("reserved key: " + key)
163 }
164
165 metas.(map[string]string)[key] = value
166
167 chain.Emit(
168 SetMetadataEvent,
169 "address", addrStr,
170 "key", key,
171 "value", value,
172 "caller", caller.String(),
173 )
174}
175
176func SetMetadata(cur realm, addr address, keyValues string) {
177 caller := runtime.PreviousRealm().Address()
178 if caller != getAdmin() && caller != addr {
179 panic("caller must be admin or the user owner")
180 }
181
182 keyValuePairs := strings.Split(keyValues, "|")
183 if len(keyValuePairs)%2 != 0 {
184 panic("keyValues must be key|value pairs")
185 }
186
187 addrStr := addr.String()
188 metas, found := metadatas.Get(addrStr)
189 if !found {
190 panic("user not found: " + addrStr)
191 }
192
193 metasMap := metas.(map[string]string)
194 for i := 0; i < len(keyValuePairs); i += 2 {
195 key := keyValuePairs[i]
196 if key == "address" {
197 continue
198 }
199 value := keyValuePairs[i+1]
200 metasMap[key] = value
201 }
202
203 chain.Emit(
204 SetMetadataEvent,
205 "address", addrStr,
206 "caller", caller.String(),
207 )
208}
209
210func ListPropertiesByAddresses(addrs ...address) []map[string]string {
211 result := []map[string]string{}
212 for _, addr := range addrs {
213 addrStr := addr.String()
214 props, found := properties.Get(addrStr)
215 if !found {
216 continue
217 }
218 propsMap := props.(map[string]string)
219 m := map[string]string{}
220 for k, v := range propsMap {
221 m[k] = v
222 }
223 result = append(result, m)
224 }
225 return result
226}
227
228func ListMetadataByAddresses(addrs ...address) []map[string]string {
229 result := []map[string]string{}
230 for _, addr := range addrs {
231 addrStr := addr.String()
232 metas, found := metadatas.Get(addrStr)
233 if !found {
234 continue
235 }
236 metasMap := metas.(map[string]string)
237 m := map[string]string{}
238 for k, v := range metasMap {
239 m[k] = v
240 }
241 result = append(result, m)
242 }
243 return result
244}
245
246func GetTotalUserSize() int {
247 return properties.Size()
248}
249
250func ListUsers(page, count int) []map[string]string {
251 if page < 1 {
252 panic("page must be at least 1")
253 }
254 if count < 1 {
255 panic("count must be at least 1")
256 }
257 if count > listLimit {
258 panic("count exceeds listLimit")
259 }
260
261 result := []map[string]string{}
262 offset := (page - 1) * count
263
264 properties.IterateByOffset(offset, count, func(key string, value interface{}) bool {
265 props := value.(map[string]string)
266 m := map[string]string{}
267 for k, v := range props {
268 m[k] = v
269 }
270 result = append(result, m)
271 return false
272 })
273
274 return result
275}