Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

hub.gno

3.70 Kb · 166 lines
  1// Package hub is the version/module router for gnomemepad.
  2//
  3// UI and tooling should resolve the active pad (and other modules) via hub
  4// instead of hardcoding padvN paths. Admins rotate module paths without
  5// redeploying every consumer.
  6//
  7//	Init → SetModule("pad", "gno.land/r/.../padv4") → GetModule("pad")
  8package hub
  9
 10import (
 11	"chain"
 12	"strings"
 13
 14	"gno.land/p/nt/avl/v0"
 15)
 16
 17var (
 18	admin  address
 19	inited bool
 20	// modules: name -> package path string
 21	modules avl.Tree
 22)
 23
 24// Init sets the hub admin (first EOA). Call once after deploy.
 25func Init(cur realm) {
 26	if inited {
 27		panic("hub: already initialized")
 28	}
 29	if !cur.Previous().IsUserCall() {
 30		panic("hub: EOA only")
 31	}
 32	admin = cur.Previous().Address()
 33	inited = true
 34	modules = avl.Tree{}
 35	chain.Emit("Init", "admin", admin.String())
 36}
 37
 38func requireInit() {
 39	if !inited {
 40		panic("hub: call Init first")
 41	}
 42}
 43
 44func requireAdmin(cur realm) {
 45	requireInit()
 46	if !cur.Previous().IsUserCall() {
 47		panic("hub: EOA only")
 48	}
 49	if cur.Previous().Address() != admin {
 50		panic("hub: not admin")
 51	}
 52}
 53
 54// SetModule registers or updates a module path (admin only).
 55// name: short key e.g. "pad", "profile", "legacy_pad"
 56// path: full gno package path
 57func SetModule(cur realm, name, path string) {
 58	requireAdmin(cur)
 59	name = strings.TrimSpace(name)
 60	path = strings.TrimSpace(path)
 61	if name == "" || len(name) > 32 {
 62		panic("hub: invalid name")
 63	}
 64	if path == "" || len(path) > 200 {
 65		panic("hub: invalid path")
 66	}
 67	if !strings.HasPrefix(path, "gno.land/") {
 68		panic("hub: path must start with gno.land/")
 69	}
 70	for _, c := range name {
 71		if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-') {
 72			panic("hub: name charset")
 73		}
 74	}
 75	modules.Set(name, path)
 76	chain.Emit("SetModule", "name", name, "path", path)
 77}
 78
 79// GetModule returns the package path for name, or "" if unset.
 80func GetModule(name string) string {
 81	requireInit()
 82	v := modules.Get(name)
 83	if v == nil {
 84		return ""
 85	}
 86	s, ok := v.(string)
 87	if !ok {
 88		return ""
 89	}
 90	return s
 91}
 92
 93// ListModules returns newline-separated "name|path" lines (sorted by name).
 94func ListModules() string {
 95	requireInit()
 96	out := ""
 97	modules.Iterate("", "", func(key string, value any) bool {
 98		path, _ := value.(string)
 99		if out != "" {
100			out += "\n"
101		}
102		out += key + "|" + path
103		return false
104	})
105	return out
106}
107
108// TransferAdmin rotates hub admin.
109func TransferAdmin(cur realm, newAdmin address) {
110	requireAdmin(cur)
111	if !newAdmin.IsValid() {
112		panic("hub: invalid address")
113	}
114	if newAdmin == admin {
115		panic("hub: same admin")
116	}
117	old := admin
118	admin = newAdmin
119	chain.Emit("TransferAdmin", "from", old.String(), "to", newAdmin.String())
120}
121
122// Admin returns the hub admin address.
123func Admin() string {
124	requireInit()
125	return admin.String()
126}
127
128// Inited reports whether Init was called.
129func Inited() bool {
130	return inited
131}
132
133// Render — discovery home.
134func Render(path string) string {
135	path = strings.Trim(path, "/")
136	if !inited {
137		return "# gnomemepad hub\n\n> Not initialized. Call Init.\n"
138	}
139	out := "# gnomemepad hub\n\n"
140	out += "- Admin: `" + admin.String() + "`\n\n"
141	out += "## Modules\n\n"
142	n := 0
143	modules.Iterate("", "", func(key string, value any) bool {
144		path, _ := value.(string)
145		out += "- **" + key + "**: `" + path + "`\n"
146		n++
147		return false
148	})
149	if n == 0 {
150		out += "_No modules registered. Admin: SetModule(name, path)._\n"
151	}
152	out += "\n## API\n\n"
153	out += "- `GetModule(name)` / `ListModules()`\n"
154	out += "- `SetModule(name, path)` — admin\n"
155	out += "- `TransferAdmin(newAdmin)` — admin\n"
156	_ = path
157	return out
158}
159
160// resetForTest — unit tests only (unexported).
161func resetForTest() {
162	var zero address
163	admin = zero
164	inited = false
165	modules = avl.Tree{}
166}