Search Apps Documentation Source Content File Folder Download Copy Actions Download

user_store.gno

6.48 Kb · 281 lines
  1package user
  2
  3import "gno.land/p/g1nqnrt3aldzhu6zzeg75yw97wvavqy7wr77g56q/deploy-test/v2/ds/btree"
  4
  5// UserStore owns user records, opaque metadata, and address/nickname indexes.
  6//
  7// Data shape:
  8// - metadatas: userID(uint32) -> metadata(string)
  9// - properties: userID(uint32) -> map[string]string
 10// - userMap: userID(uint32) -> address(string)
 11// - addressIDs: address(string) -> userID(uint32)
 12// - nicknames: userID(uint32) -> nickname(string)
 13// - nicknameIDs: nickname(string) -> userID(uint32)
 14type UserStore struct {
 15	metadatas   *btree.Uint32BTree
 16	properties  *btree.Uint32BTree
 17	userMap     *btree.Uint32BTree
 18	addressIDs  *btree.StringBTree
 19	nicknames   *btree.Uint32BTree
 20	nicknameIDs *btree.StringBTree
 21	nextUserID  uint32
 22}
 23
 24var userStore = newUserStore()
 25
 26func newUserStore() *UserStore {
 27	return &UserStore{
 28		metadatas:   btree.NewUint32BTree(32),
 29		properties:  btree.NewUint32BTree(32),
 30		userMap:     btree.NewUint32BTree(32),
 31		addressIDs:  btree.NewStringBTree(32),
 32		nicknames:   btree.NewUint32BTree(32),
 33		nicknameIDs: btree.NewStringBTree(32),
 34		nextUserID:  1,
 35	}
 36}
 37
 38func (s *UserStore) Has(addr address) bool {
 39	_, found := s.addressIDs.Get(addr.String())
 40	return found
 41}
 42
 43func (s *UserStore) Create(addr address, props map[string]string) {
 44	addrStr := addr.String()
 45	s.assertNotExists(addrStr)
 46	nickname := props["nickname"]
 47	s.assertNicknameAvailable(nickname)
 48
 49	id := s.createID(addr)
 50	s.setNickname(id, nickname)
 51	s.properties.Set(id, copyStringMap(props))
 52}
 53
 54func (s *UserStore) Update(addr address, props map[string]string) map[string]string {
 55	addrStr := addr.String()
 56	s.assertExists(addrStr)
 57
 58	id := s.getID(addr)
 59	currentVal, found := s.properties.Get(id)
 60	if !found {
 61		panic("user properties not found: " + addrStr)
 62	}
 63
 64	if nickname, found := props["nickname"]; found {
 65		currentNickname := s.getNickname(id)
 66		if currentNickname != nickname {
 67			s.assertNicknameAvailable(nickname)
 68			s.unsetNickname(id)
 69			s.setNickname(id, nickname)
 70		}
 71	}
 72
 73	current := currentVal.(map[string]string)
 74	for key, value := range props {
 75		current[key] = value
 76	}
 77	s.properties.Set(id, current)
 78	return copyStringMap(current)
 79}
 80
 81func (s *UserStore) Delete(addr address) {
 82	addrStr := addr.String()
 83	s.assertExists(addrStr)
 84
 85	id := s.getID(addr)
 86	s.unsetNickname(id)
 87	s.userMap.Remove(id)
 88	s.addressIDs.Remove(addrStr)
 89	s.metadatas.Remove(id)
 90	s.properties.Remove(id)
 91}
 92
 93func (s *UserStore) SetMetadata(addr address, metadata string) {
 94	id := s.getID(addr)
 95	s.metadatas.Set(id, metadata)
 96}
 97
 98func (s *UserStore) Total() int {
 99	return s.userMap.Size()
100}
101
102func (s *UserStore) NextID() uint32 {
103	return s.nextUserID
104}
105
106// ==================== DANGER: MUTABLE MIGRATION CAPABILITIES ====================
107//
108// The getters in this section expose mutable store internals.
109// Only authorized migration exporter paths may depend on these capabilities.
110// Runtime reads and test setup must use copy-returning methods or total helpers.
111func (s *UserStore) Metadatas() *btree.Uint32BTree {
112	return s.metadatas
113}
114
115func (s *UserStore) Properties() *btree.Uint32BTree {
116	return s.properties
117}
118
119func (s *UserStore) UserMap() *btree.Uint32BTree {
120	return s.userMap
121}
122
123func (s *UserStore) AddressIDs() *btree.StringBTree {
124	return s.addressIDs
125}
126
127func (s *UserStore) Nicknames() *btree.Uint32BTree {
128	return s.nicknames
129}
130
131func (s *UserStore) NicknameIDs() *btree.StringBTree {
132	return s.nicknameIDs
133}
134
135// ================= END DANGER: MUTABLE MIGRATION CAPABILITIES ==================
136
137func (s *UserStore) List(page, count int) []string {
138	result := make([]string, 0)
139	offset := (page - 1) * count
140
141	s.userMap.IterateByOffset(offset, count, func(id uint32, val interface{}) bool {
142		result = append(result, val.(string))
143		return false
144	})
145
146	return result
147}
148
149func (s *UserStore) AddressByNickname(nickname string) address {
150	v, found := s.nicknameIDs.Get(nickname)
151	if !found {
152		panic("user not found")
153	}
154	return s.addressByID(v.(uint32))
155}
156
157func (s *UserStore) HasNickname(nickname string) bool {
158	_, found := s.nicknameIDs.Get(nickname)
159	return found
160}
161
162func (s *UserStore) NicknameByAddress(addr address) string {
163	id, found := s.findID(addr.String())
164	if !found {
165		return ""
166	}
167	return s.getNickname(id)
168}
169
170func (s *UserStore) ListUserMetadataByAddresses(addrs ...address) []string {
171	result := []string{}
172	for _, addr := range addrs {
173		id, userFound := s.findID(addr.String())
174		if !userFound {
175			result = append(result, "")
176			continue
177		}
178		metadata, found := s.metadatas.Get(id)
179		if !found {
180			result = append(result, "")
181			continue
182		}
183		result = append(result, metadata.(string))
184	}
185	return result
186}
187
188func (s *UserStore) ListUsersByAddresses(addrs ...address) []map[string]string {
189	result := []map[string]string{}
190	for _, addr := range addrs {
191		addrStr := addr.String()
192		id, userFound := s.findID(addrStr)
193		if !userFound {
194			continue
195		}
196		props, found := s.properties.Get(id)
197		if !found {
198			continue
199		}
200		m := copyStringMap(props.(map[string]string))
201		m["address"] = addrStr
202		result = append(result, m)
203	}
204	return result
205}
206
207func (s *UserStore) assertNotExists(addrStr string) {
208	_, found := s.addressIDs.Get(addrStr)
209	if found {
210		panic("user already exists: " + addrStr)
211	}
212}
213
214func (s *UserStore) assertExists(addrStr string) {
215	_, found := s.addressIDs.Get(addrStr)
216	if !found {
217		panic("user not found: " + addrStr)
218	}
219}
220
221func (s *UserStore) assertNicknameAvailable(nickname string) {
222	_, taken := s.nicknameIDs.Get(nickname)
223	if taken {
224		panic("nickname already exists: " + nickname)
225	}
226}
227
228func (s *UserStore) createID(addr address) uint32 {
229	id := s.nextUserID
230	s.nextUserID++
231	s.userMap.Set(id, addr.String())
232	s.addressIDs.Set(addr.String(), id)
233	return id
234}
235
236func (s *UserStore) findID(addrStr string) (uint32, bool) {
237	v, found := s.addressIDs.Get(addrStr)
238	if !found {
239		return 0, false
240	}
241	return v.(uint32), true
242}
243
244func (s *UserStore) getID(addr address) uint32 {
245	addrStr := addr.String()
246	id, found := s.findID(addrStr)
247	if !found {
248		panic("user not found: " + addrStr)
249	}
250	return id
251}
252
253func (s *UserStore) addressByID(id uint32) address {
254	v, found := s.userMap.Get(id)
255	if !found {
256		panic("user not found")
257	}
258	return address(v.(string))
259}
260
261func (s *UserStore) getNickname(id uint32) string {
262	v, found := s.nicknames.Get(id)
263	if !found {
264		return ""
265	}
266	return v.(string)
267}
268
269func (s *UserStore) setNickname(id uint32, nickname string) {
270	s.nicknames.Set(id, nickname)
271	s.nicknameIDs.Set(nickname, id)
272}
273
274func (s *UserStore) unsetNickname(id uint32) {
275	oldNick, found := s.nicknames.Get(id)
276	if !found {
277		return
278	}
279	s.nicknames.Remove(id)
280	s.nicknameIDs.Remove(oldNick.(string))
281}