Search Apps Documentation Source Content File Folder Download Copy Actions Download

blueprint_store.gno

13.05 Kb · 478 lines
  1package blueprint
  2
  3import (
  4	"gno.land/p/akkadia/v0/ds/btree"
  5	"gno.land/p/akkadia/v0/ds/btreeset"
  6)
  7
  8var blueprintStore *BlueprintStore = newBlueprintStore()
  9
 10// BlueprintStore owns blueprint records, opaque metadata, and secondary indexes.
 11//
 12// Data shape:
 13// - blueprints: blueprintID(uint32) -> map[string]string
 14// - metadatas: blueprintID(uint32) -> metadata(string)
 15// - nameIndex: name(string) -> blueprintID(uint32)
 16// - ownerIndex: owner(address string) -> *Uint32BTreeSet(blueprintID)
 17// - biomeIndex: biome(string) -> *Uint32BTreeSet(blueprintID)
 18// - stateIndex: state(string) -> *Uint32BTreeSet(blueprintID)
 19// - biomeStateIndex: biome(string) -> *StringBTree(state(string) -> *Uint32BTreeSet(blueprintID))
 20//
 21// Responsibilities:
 22// - allocate blueprint IDs and store canonical blueprint records
 23// - keep name/owner/biome/state/biome-state indexes in sync with record mutations
 24// - store per-blueprint admin-managed opaque metadata text
 25// - protect store-owned invariants such as duplicate names
 26// - return defensive copies from public read methods
 27//
 28// Non-responsibilities:
 29// - validate external parameter shape such as name format, CSV payloads, or pagination
 30// - interpret or semantically validate metadata; metadata is off-chain decoded admin text
 31// - perform auth, payment, freeze, migration, or event handling
 32// - parse user-facing encoded input; public exported functions must pass canonical values
 33type BlueprintStore struct {
 34	nextBlueprintID uint32
 35	blueprints      *btree.Uint32BTree
 36	metadatas       *btree.Uint32BTree
 37	nameIndex       *btree.StringBTree
 38	ownerIndex      *btree.StringBTree
 39	biomeIndex      *btree.StringBTree
 40	stateIndex      *btree.StringBTree
 41	biomeStateIndex *btree.StringBTree
 42}
 43
 44func newBlueprintStore() *BlueprintStore {
 45	return &BlueprintStore{
 46		nextBlueprintID: 1,
 47		blueprints:      btree.NewUint32BTree(32),
 48		metadatas:       btree.NewUint32BTree(32),
 49		nameIndex:       btree.NewStringBTree(32),
 50		ownerIndex:      btree.NewStringBTree(32),
 51		biomeIndex:      btree.NewStringBTree(32),
 52		stateIndex:      btree.NewStringBTree(32),
 53		biomeStateIndex: btree.NewStringBTree(32),
 54	}
 55}
 56
 57func (s *BlueprintStore) NextID() uint32 {
 58	return s.nextBlueprintID
 59}
 60
 61// ==================== DANGER: MUTABLE MIGRATION CAPABILITIES ====================
 62//
 63// The getters in this section expose mutable store internals.
 64// Only authorized migration exporter paths may depend on these capabilities.
 65// Runtime reads and test setup must use copy-returning methods or total helpers.
 66func (s *BlueprintStore) Blueprints() *btree.Uint32BTree {
 67	return s.blueprints
 68}
 69
 70func (s *BlueprintStore) Metadatas() *btree.Uint32BTree {
 71	return s.metadatas
 72}
 73
 74func (s *BlueprintStore) NameIndex() *btree.StringBTree {
 75	return s.nameIndex
 76}
 77
 78func (s *BlueprintStore) OwnerIndex() *btree.StringBTree {
 79	return s.ownerIndex
 80}
 81
 82func (s *BlueprintStore) BiomeIndex() *btree.StringBTree {
 83	return s.biomeIndex
 84}
 85
 86func (s *BlueprintStore) StateIndex() *btree.StringBTree {
 87	return s.stateIndex
 88}
 89
 90func (s *BlueprintStore) BiomeStateIndex() *btree.StringBTree {
 91	return s.biomeStateIndex
 92}
 93
 94// ================= END DANGER: MUTABLE MIGRATION CAPABILITIES ==================
 95
 96func (s *BlueprintStore) Create(blueprint map[string]string) uint32 {
 97	name := blueprint["name"]
 98	if s.nameIndex.Has(name) {
 99		panic("name already exists")
100	}
101
102	id := s.nextID()
103	stored := copyStringMap(blueprint)
104	stored["id"] = formatBlueprintID(id)
105	s.blueprints.Set(id, stored)
106	s.nameIndex.Set(name, id)
107	s.addOwnerIndex(id, address(stored["owner"]))
108	s.addBiomeIndex(id, stored["biome"])
109	s.addStateIndex(id, stored["state"])
110	s.addBiomeStateIndex(id, stored["biome"], stored["state"])
111	return id
112}
113
114func (s *BlueprintStore) Update(blueprintID uint32, updates map[string]string) map[string]string {
115	current := s.mustGetInternal(blueprintID)
116	oldName := current["name"]
117	if name, found := updates["name"]; found {
118		if oldName != name && s.nameIndex.Has(name) {
119			panic("name already exists")
120		}
121	}
122
123	updated := copyStringMap(current)
124	for key, value := range updates {
125		updated[key] = value
126	}
127	updated["id"] = formatBlueprintID(blueprintID)
128
129	oldOwner := current["owner"]
130	newOwner := updated["owner"]
131	newName := updated["name"]
132	oldBiome := current["biome"]
133	newBiome := updated["biome"]
134	oldState := current["state"]
135	newState := updated["state"]
136
137	if oldOwner != newOwner {
138		s.removeOwnerIndex(blueprintID, address(oldOwner))
139		s.addOwnerIndex(blueprintID, address(newOwner))
140	}
141	if oldName != newName {
142		s.nameIndex.Remove(oldName)
143		s.nameIndex.Set(newName, blueprintID)
144	}
145	if oldBiome != newBiome {
146		s.removeBiomeIndex(blueprintID, oldBiome)
147		s.addBiomeIndex(blueprintID, newBiome)
148	}
149	if oldState != newState {
150		s.removeStateIndex(blueprintID, oldState)
151		s.addStateIndex(blueprintID, newState)
152	}
153	if oldBiome != newBiome || oldState != newState {
154		s.removeBiomeStateIndex(blueprintID, oldBiome, oldState)
155		s.addBiomeStateIndex(blueprintID, newBiome, newState)
156	}
157
158	s.blueprints.Set(blueprintID, updated)
159	return copyStringMap(updated)
160}
161
162func (s *BlueprintStore) Delete(blueprintID uint32) {
163	blueprint := s.mustGetInternal(blueprintID)
164	s.blueprints.Remove(blueprintID)
165	s.nameIndex.Remove(blueprint["name"])
166	s.removeOwnerIndex(blueprintID, address(blueprint["owner"]))
167	s.removeBiomeIndex(blueprintID, blueprint["biome"])
168	s.removeStateIndex(blueprintID, blueprint["state"])
169	s.removeBiomeStateIndex(blueprintID, blueprint["biome"], blueprint["state"])
170	s.metadatas.Remove(blueprintID)
171}
172
173func (s *BlueprintStore) SetMetadata(blueprintID uint32, metadata string) {
174	s.mustGetInternal(blueprintID)
175	s.metadatas.Set(blueprintID, metadata)
176}
177
178func (s *BlueprintStore) GetMetadata(blueprintID uint32) string {
179	s.mustGetInternal(blueprintID)
180	metadata, found := s.metadatas.Get(blueprintID)
181	if !found {
182		return ""
183	}
184	return metadata.(string)
185}
186
187func (s *BlueprintStore) ListMetadataByIDs(blueprintIDs ...uint32) []string {
188	result := []string{}
189	for _, blueprintID := range blueprintIDs {
190		if !s.blueprints.Has(blueprintID) {
191			continue
192		}
193		metadata, found := s.metadatas.Get(blueprintID)
194		if !found {
195			result = append(result, "")
196			continue
197		}
198		result = append(result, metadata.(string))
199	}
200	return result
201}
202
203func (s *BlueprintStore) MustGet(blueprintID uint32) map[string]string {
204	return copyStringMap(s.mustGetInternal(blueprintID))
205}
206
207func (s *BlueprintStore) Get(blueprintID uint32) (map[string]string, bool) {
208	blueprint, found := s.blueprints.Get(blueprintID)
209	if !found {
210		return nil, false
211	}
212	return copyStringMap(blueprint.(map[string]string)), true
213}
214
215func (s *BlueprintStore) AssertBlueprintExists(blueprintID uint32) {
216	if !s.blueprints.Has(blueprintID) {
217		panic("blueprint not found: " + formatBlueprintID(blueprintID))
218	}
219}
220
221func (s *BlueprintStore) mustGetInternal(blueprintID uint32) map[string]string {
222	properties, found := s.blueprints.Get(blueprintID)
223	if !found {
224		panic("blueprint not found: " + formatBlueprintID(blueprintID))
225	}
226	return properties.(map[string]string)
227}
228
229func (s *BlueprintStore) MustGetIDByName(name string) uint32 {
230	id, found := s.nameIndex.Get(name)
231	if !found {
232		panic("blueprint not found by name: " + name)
233	}
234	return id.(uint32)
235}
236
237func (s *BlueprintStore) Total() int {
238	return s.blueprints.Size()
239}
240
241func (s *BlueprintStore) IsNameAvailable(name string) bool {
242	return !s.nameIndex.Has(name)
243}
244
245func (s *BlueprintStore) ListIDs(page int, count int) []uint32 {
246	offset := (page - 1) * count
247	result := []uint32{}
248	s.blueprints.IterateByOffset(offset, count, func(blueprintID uint32, _ any) bool {
249		result = append(result, blueprintID)
250		return false
251	})
252	return result
253}
254
255func (s *BlueprintStore) ListByIDs(blueprintIDs ...uint32) []map[string]string {
256	result := []map[string]string{}
257	for _, blueprintID := range blueprintIDs {
258		blueprint, found := s.Get(blueprintID)
259		if !found {
260			continue
261		}
262		result = append(result, blueprint)
263	}
264	return result
265}
266
267func (s *BlueprintStore) OwnerCount(owner address) int {
268	val, found := s.ownerIndex.Get(owner.String())
269	if !found {
270		return 0
271	}
272	return val.(*btreeset.Uint32BTreeSet).Size()
273}
274
275func (s *BlueprintStore) ListIDsByOwner(owner address, page int, count int) []uint32 {
276	val, found := s.ownerIndex.Get(owner.String())
277	if !found {
278		return []uint32{}
279	}
280	offset := (page - 1) * count
281	result := []uint32{}
282	val.(*btreeset.Uint32BTreeSet).IterateByOffset(offset, count, func(id uint32) bool {
283		result = append(result, id)
284		return false
285	})
286	return result
287}
288
289func (s *BlueprintStore) BiomeCount(biomeName string) int {
290	val, found := s.biomeIndex.Get(biomeName)
291	if !found {
292		return 0
293	}
294	return val.(*btreeset.Uint32BTreeSet).Size()
295}
296
297func (s *BlueprintStore) ListIDsByBiome(biomeName string, page int, count int) []uint32 {
298	val, found := s.biomeIndex.Get(biomeName)
299	if !found {
300		return []uint32{}
301	}
302	offset := (page - 1) * count
303	result := []uint32{}
304	val.(*btreeset.Uint32BTreeSet).IterateByOffset(offset, count, func(id uint32) bool {
305		result = append(result, id)
306		return false
307	})
308	return result
309}
310
311func (s *BlueprintStore) StateCount(state string) int {
312	val, found := s.stateIndex.Get(state)
313	if !found {
314		return 0
315	}
316	return val.(*btreeset.Uint32BTreeSet).Size()
317}
318
319func (s *BlueprintStore) ListIDsByState(state string, page int, count int) []uint32 {
320	val, found := s.stateIndex.Get(state)
321	if !found {
322		return []uint32{}
323	}
324	offset := (page - 1) * count
325	result := []uint32{}
326	val.(*btreeset.Uint32BTreeSet).IterateByOffset(offset, count, func(id uint32) bool {
327		result = append(result, id)
328		return false
329	})
330	return result
331}
332
333func (s *BlueprintStore) BiomeStateCount(biomeName string, state string) int {
334	stateTree, found := s.biomeStateTree(biomeName)
335	if !found {
336		return 0
337	}
338	val, found := stateTree.Get(state)
339	if !found {
340		return 0
341	}
342	return val.(*btreeset.Uint32BTreeSet).Size()
343}
344
345func (s *BlueprintStore) ListIDsByBiomeState(biomeName string, state string, page int, count int) []uint32 {
346	stateTree, found := s.biomeStateTree(biomeName)
347	if !found {
348		return []uint32{}
349	}
350	val, found := stateTree.Get(state)
351	if !found {
352		return []uint32{}
353	}
354
355	offset := (page - 1) * count
356	result := []uint32{}
357	val.(*btreeset.Uint32BTreeSet).IterateByOffset(offset, count, func(blueprintID uint32) bool {
358		result = append(result, blueprintID)
359		return false
360	})
361	return result
362}
363
364func (s *BlueprintStore) IsOwner(blueprintID uint32, user address) bool {
365	blueprint := s.mustGetInternal(blueprintID)
366	return address(blueprint["owner"]) == user
367}
368
369func (s *BlueprintStore) nextID() uint32 {
370	id := s.nextBlueprintID
371	s.nextBlueprintID++
372	return id
373}
374
375func (s *BlueprintStore) addOwnerIndex(blueprintID uint32, owner address) {
376	ownerKey := owner.String()
377	var tree *btreeset.Uint32BTreeSet
378	val, found := s.ownerIndex.Get(ownerKey)
379	if !found {
380		tree = btreeset.NewUint32BTreeSet(16)
381		s.ownerIndex.Set(ownerKey, tree)
382	} else {
383		tree = val.(*btreeset.Uint32BTreeSet)
384	}
385	tree.Set(blueprintID)
386}
387
388func (s *BlueprintStore) removeOwnerIndex(blueprintID uint32, owner address) {
389	val, found := s.ownerIndex.Get(owner.String())
390	if !found {
391		return
392	}
393	val.(*btreeset.Uint32BTreeSet).Remove(blueprintID)
394}
395
396func (s *BlueprintStore) addBiomeIndex(blueprintID uint32, biomeName string) {
397	var tree *btreeset.Uint32BTreeSet
398	val, found := s.biomeIndex.Get(biomeName)
399	if !found {
400		tree = btreeset.NewUint32BTreeSet(16)
401		s.biomeIndex.Set(biomeName, tree)
402	} else {
403		tree = val.(*btreeset.Uint32BTreeSet)
404	}
405	tree.Set(blueprintID)
406}
407
408func (s *BlueprintStore) removeBiomeIndex(blueprintID uint32, biomeName string) {
409	val, found := s.biomeIndex.Get(biomeName)
410	if !found {
411		return
412	}
413	val.(*btreeset.Uint32BTreeSet).Remove(blueprintID)
414}
415
416func (s *BlueprintStore) addStateIndex(blueprintID uint32, state string) {
417	var tree *btreeset.Uint32BTreeSet
418	val, found := s.stateIndex.Get(state)
419	if !found {
420		tree = btreeset.NewUint32BTreeSet(16)
421		s.stateIndex.Set(state, tree)
422	} else {
423		tree = val.(*btreeset.Uint32BTreeSet)
424	}
425	tree.Set(blueprintID)
426}
427
428func (s *BlueprintStore) removeStateIndex(blueprintID uint32, state string) {
429	val, found := s.stateIndex.Get(state)
430	if !found {
431		return
432	}
433	val.(*btreeset.Uint32BTreeSet).Remove(blueprintID)
434}
435
436func (s *BlueprintStore) addBiomeStateIndex(blueprintID uint32, biomeName string, state string) {
437	stateTree := s.getOrCreateBiomeStateTree(biomeName)
438
439	var set *btreeset.Uint32BTreeSet
440	val, found := stateTree.Get(state)
441	if !found {
442		set = btreeset.NewUint32BTreeSet(16)
443		stateTree.Set(state, set)
444	} else {
445		set = val.(*btreeset.Uint32BTreeSet)
446	}
447	set.Set(blueprintID)
448}
449
450func (s *BlueprintStore) removeBiomeStateIndex(blueprintID uint32, biomeName string, state string) {
451	stateTree, found := s.biomeStateTree(biomeName)
452	if !found {
453		return
454	}
455	val, found := stateTree.Get(state)
456	if !found {
457		return
458	}
459	val.(*btreeset.Uint32BTreeSet).Remove(blueprintID)
460}
461
462func (s *BlueprintStore) biomeStateTree(biomeName string) (*btree.StringBTree, bool) {
463	val, found := s.biomeStateIndex.Get(biomeName)
464	if !found {
465		return nil, false
466	}
467	return val.(*btree.StringBTree), true
468}
469
470func (s *BlueprintStore) getOrCreateBiomeStateTree(biomeName string) *btree.StringBTree {
471	tree, found := s.biomeStateTree(biomeName)
472	if found {
473		return tree
474	}
475	tree = btree.NewStringBTree(16)
476	s.biomeStateIndex.Set(biomeName, tree)
477	return tree
478}