Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}