extensions.gno
4.03 Kb · 137 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/nt/avl/v0"
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/demo/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 Private bool // If true, extension is only accessible from the same realm that registered it
23}
24
25type ExtensionsList interface {
26 // Returns the total number of registered extensions.
27 Len() int
28 // Returns extension info at the specified index, or nil if index is out of bounds.
29 Get(index int) *ExtensionInfo
30 // Returns a slice of ExtensionInfo from startIndex to endIndex.
31 Slice(startIndex, endIndex int) []ExtensionInfo
32 // Iterates over all extensions, calling fn for each one.
33 // Iteration stops if fn returns true.
34 ForEach(fn func(index int, value ExtensionInfo) bool)
35}
36
37type ExtensionsStore struct {
38 Tree avl.Tree
39}
40
41// Registers a new extension using its path as the key.
42// Returns true if the extension was successfully registered.
43// If an extension with the same path already exists, it will be replaced.
44func (es *ExtensionsStore) Set(ext Extension) bool {
45 info := ext.Info()
46 return es.Tree.Set(info.Path, ext)
47}
48
49func (es *ExtensionsStore) Get(path string) (Extension, bool) {
50 iface, ok := es.Tree.Get(path)
51 if !ok {
52 return nil, false
53 }
54 return iface.(Extension), true
55}
56
57// Remove unregisters an extension by its path.
58// Returns the removed extension and true if found, nil and false otherwise.
59func (es *ExtensionsStore) Remove(path string) (Extension, bool) {
60 iface, ok := es.Tree.Remove(path)
61 if !ok {
62 return nil, false
63 }
64 return iface.(Extension), true
65}
66
67// Returns an ExtensionsList interface for iterating over all registered extensions.
68// This provides read-only access to the extensions registry.
69func (es *ExtensionsStore) List() ExtensionsList {
70 return &extensionsList{es: es}
71}
72
73type extensionsList struct {
74 es *ExtensionsStore
75}
76
77// Iterates over all extensions, calling fn for each extension with its index and info.
78// Iteration stops early if fn returns true.
79func (e *extensionsList) ForEach(fn func(index int, value ExtensionInfo) bool) {
80 if e.es.Tree.Size() == 0 {
81 return
82 }
83
84 index := 0
85 e.es.Tree.IterateByOffset(0, e.es.Tree.Size(), func(_ string, value any) bool {
86 result := fn(index, value.(Extension).Info())
87 index++
88 return result
89 })
90}
91
92// Returns the ExtensionInfo at the specified index.
93func (e *extensionsList) Get(index int) *ExtensionInfo {
94 if index < 0 || index >= e.es.Tree.Size() {
95 return nil
96 }
97
98 _, value := e.es.Tree.GetByIndex(index)
99 info := value.(Extension).Info()
100 return &info
101}
102
103// Returns the total number of registered extensions.
104func (e *extensionsList) Len() int {
105 return e.es.Tree.Size()
106}
107
108// Returns a slice of ExtensionInfo from startIndex (inclusive) to endIndex (exclusive).
109// Bounds are automatically normalized: negative startIndex becomes 0, endIndex beyond size becomes size.
110func (e *extensionsList) Slice(startIndex int, endIndex int) []ExtensionInfo {
111 size := e.es.Tree.Size()
112
113 // Normalize bounds
114 if startIndex < 0 {
115 startIndex = 0
116 }
117
118 if endIndex > size {
119 endIndex = size
120 }
121
122 if startIndex >= endIndex {
123 return nil
124 }
125
126 count := endIndex - startIndex
127 result := make([]ExtensionInfo, count)
128
129 i := 0
130 e.es.Tree.IterateByOffset(startIndex, count, func(_ string, value any) bool {
131 result[i] = value.(Extension).Info()
132 i++
133 return false
134 })
135
136 return result
137}