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}