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

extensions.gno

4.37 Kb · 143 lines
  1package daokit
  2
  3// Extensions system enables DAOs to expose additional functionality that can be
  4// accessed by other packages or realms. It provides a secure way to make
  5// specific DAO capabilities available without exposing internal implementation details.
  6
  7import "gno.land/p/samcrew/avl"
  8
  9// Interface must be implemented by any object that wants to be
 10// registered as a DAO extension. Extensions expose functionality through
 11// well-defined interfaces while maintaining encapsulation.
 12type Extension interface {
 13	// Returns metadata about this extension including its path, version,
 14	// query path for external access, and privacy settings.
 15	Info() ExtensionInfo
 16}
 17
 18type ExtensionInfo struct {
 19	Path      string // Unique extension identifier (e.g., "gno.land/p/samcrew/basedao.MembersView")
 20	Version   string // Extension version (e.g., "1", "2.0", etc.)
 21	QueryPath string // Path for external queries to access this extension's data
 22	// If true, only the registering realm can obtain the extension VALUE
 23	// through DAO.Extension — which is a capability, not a secret. Realm state
 24	// on a public chain is world-readable over ABCI regardless, and this
 25	// ExtensionInfo (including QueryPath) is listed by ExtensionsList without a
 26	// check. Private restricts who can call the extension's methods under the
 27	// DAO's authority; it does not hide anything.
 28	Private bool
 29}
 30
 31type ExtensionsList interface {
 32	// Returns the total number of registered extensions.
 33	Len() int
 34	// Returns extension info at the specified index, or nil if index is out of bounds.
 35	Get(index int) *ExtensionInfo
 36	// Returns a slice of ExtensionInfo from startIndex to endIndex.
 37	Slice(startIndex, endIndex int) []ExtensionInfo
 38	// Iterates over all extensions, calling fn for each one.
 39	// Iteration stops if fn returns true.
 40	ForEach(fn func(index int, value ExtensionInfo) bool)
 41}
 42
 43type ExtensionsStore struct {
 44	Tree avl.Tree
 45}
 46
 47// Registers a new extension using its path as the key.
 48// Returns true if the extension was successfully registered.
 49// If an extension with the same path already exists, it will be replaced.
 50func (es *ExtensionsStore) Set(ext Extension) bool {
 51	info := ext.Info()
 52	return es.Tree.Set(info.Path, ext)
 53}
 54
 55func (es *ExtensionsStore) Get(path string) (Extension, bool) {
 56	iface, ok := es.Tree.Get(path)
 57	if !ok {
 58		return nil, false
 59	}
 60	return iface.(Extension), true
 61}
 62
 63// Remove unregisters an extension by its path.
 64// Returns the removed extension and true if found, nil and false otherwise.
 65func (es *ExtensionsStore) Remove(path string) (Extension, bool) {
 66	iface, ok := es.Tree.Remove(path)
 67	if !ok {
 68		return nil, false
 69	}
 70	return iface.(Extension), true
 71}
 72
 73// Returns an ExtensionsList interface for iterating over all registered extensions.
 74// This provides read-only access to the extensions registry.
 75func (es *ExtensionsStore) List() ExtensionsList {
 76	return &extensionsList{es: es}
 77}
 78
 79type extensionsList struct {
 80	es *ExtensionsStore
 81}
 82
 83// Iterates over all extensions, calling fn for each extension with its index and info.
 84// Iteration stops early if fn returns true.
 85func (e *extensionsList) ForEach(fn func(index int, value ExtensionInfo) bool) {
 86	if e.es.Tree.Size() == 0 {
 87		return
 88	}
 89
 90	index := 0
 91	e.es.Tree.IterateByOffset(0, e.es.Tree.Size(), func(_ string, value any) bool {
 92		result := fn(index, value.(Extension).Info())
 93		index++
 94		return result
 95	})
 96}
 97
 98// Returns the ExtensionInfo at the specified index.
 99func (e *extensionsList) Get(index int) *ExtensionInfo {
100	if index < 0 || index >= e.es.Tree.Size() {
101		return nil
102	}
103
104	_, value := e.es.Tree.GetByIndex(index)
105	info := value.(Extension).Info()
106	return &info
107}
108
109// Returns the total number of registered extensions.
110func (e *extensionsList) Len() int {
111	return e.es.Tree.Size()
112}
113
114// Returns a slice of ExtensionInfo from startIndex (inclusive) to endIndex (exclusive).
115// Bounds are automatically normalized: negative startIndex becomes 0, endIndex beyond size becomes size.
116func (e *extensionsList) Slice(startIndex int, endIndex int) []ExtensionInfo {
117	size := e.es.Tree.Size()
118
119	// Normalize bounds
120	if startIndex < 0 {
121		startIndex = 0
122	}
123
124	if endIndex > size {
125		endIndex = size
126	}
127
128	if startIndex >= endIndex {
129		return nil
130	}
131
132	count := endIndex - startIndex
133	result := make([]ExtensionInfo, count)
134
135	i := 0
136	e.es.Tree.IterateByOffset(startIndex, count, func(_ string, value any) bool {
137		result[i] = value.(Extension).Info()
138		i++
139		return false
140	})
141
142	return result
143}