Search Apps Documentation Source Content File Folder Download Copy Actions Download

vault.gno

2.86 Kb · 116 lines
  1package vault
  2
  3import (
  4	"chain"
  5	"chain/runtime/unsafe"
  6	"strconv"
  7	"strings"
  8
  9	"gno.land/p/nt/avl/v0"
 10)
 11
 12// vaults maps address string -> *avl.Tree (key string -> value string).
 13var vaults avl.Tree
 14
 15// vaultFor returns the caller's namespace tree, creating it if needed.
 16func vaultFor(owner address) *avl.Tree {
 17	k := owner.String()
 18	if v, ok := vaults.Get(k); ok {
 19		return v.(*avl.Tree)
 20	}
 21	t := &avl.Tree{}
 22	vaults.Set(k, t)
 23	return t
 24}
 25
 26// Set stores value under key in the caller's own namespace.
 27func Set(cur realm, key string, value string) {
 28	if key == "" {
 29		panic("key must not be empty")
 30	}
 31	caller := unsafe.PreviousRealm().Address()
 32	t := vaultFor(caller)
 33	t.Set(key, value)
 34	chain.Emit("VaultSet", "owner", caller.String(), "key", key)
 35}
 36
 37// Delete removes key from the caller's own namespace.
 38func Delete(cur realm, key string) {
 39	caller := unsafe.PreviousRealm().Address()
 40	k := caller.String()
 41	v, ok := vaults.Get(k)
 42	if !ok {
 43		panic("no vault for caller")
 44	}
 45	t := v.(*avl.Tree)
 46	if _, removed := t.Remove(key); !removed {
 47		panic("key not found")
 48	}
 49	if t.Size() == 0 {
 50		vaults.Remove(k)
 51	}
 52	chain.Emit("VaultDelete", "owner", caller.String(), "key", key)
 53}
 54
 55// Render shows all vaults (root) or a single address's entries ("/<address>").
 56func Render(path string) string {
 57	p := strings.TrimPrefix(path, "/")
 58	if p == "" {
 59		return renderRoot()
 60	}
 61	return renderAddress(p)
 62}
 63
 64func renderRoot() string {
 65	var b strings.Builder
 66	b.WriteString("# Key-Value Vault\n\n")
 67	b.WriteString("A per-address key-value store. Each caller owns their own namespace; only the owner can mutate their entries.\n\n")
 68	if vaults.Size() == 0 {
 69		b.WriteString("_No vaults yet. Call `Set(cur, key, value)` to create one._\n")
 70		return b.String()
 71	}
 72	b.WriteString("## Vaults\n\n")
 73	b.WriteString("| Address | Entries |\n")
 74	b.WriteString("|---|---|\n")
 75	vaults.Iterate("", "", func(k string, v interface{}) bool {
 76		t := v.(*avl.Tree)
 77		b.WriteString("| [")
 78		b.WriteString(k)
 79		b.WriteString("](/r:")
 80		// link back into this realm's render for the address subpage
 81		b.WriteString(k)
 82		b.WriteString(") | ")
 83		b.WriteString(strconv.Itoa(t.Size()))
 84		b.WriteString(" |\n")
 85		return false
 86	})
 87	b.WriteString("\nView an address's entries at path `/<address>`.\n")
 88	return b.String()
 89}
 90
 91func renderAddress(addr string) string {
 92	var b strings.Builder
 93	b.WriteString("# Vault: ")
 94	b.WriteString(addr)
 95	b.WriteString("\n\n")
 96	v, ok := vaults.Get(addr)
 97	if !ok {
 98		b.WriteString("_No vault for this address._\n")
 99		return b.String()
100	}
101	t := v.(*avl.Tree)
102	b.WriteString("| Key | Value |\n")
103	b.WriteString("|---|---|\n")
104	t.Iterate("", "", func(k string, val interface{}) bool {
105		b.WriteString("| ")
106		b.WriteString(k)
107		b.WriteString(" | ")
108		b.WriteString(val.(string))
109		b.WriteString(" |\n")
110		return false
111	})
112	b.WriteString("\nTotal entries: ")
113	b.WriteString(strconv.Itoa(t.Size()))
114	b.WriteString("\n")
115	return b.String()
116}