rotree.gno
4.97 Kb · 151 lines
1// Package rotree provides a read-only wrapper for bptree.BPTree with safe value transformation.
2//
3// It is useful when you want to expose a read-only view of a tree while ensuring that
4// the sensitive data cannot be modified.
5//
6// Example:
7//
8// // Define a user structure with sensitive data
9// type User struct {
10// Name string
11// Balance int
12// Internal string // sensitive field
13// }
14//
15// // Create and populate the original tree
16// privateTree := bptree.NewBPTree32()
17// privateTree.Set("alice", &User{
18// Name: "Alice",
19// Balance: 100,
20// Internal: "sensitive",
21// })
22//
23// // Create a safe transformation function that copies the struct
24// // while excluding sensitive data
25// makeEntrySafeFn := func(v any) any {
26// u := v.(*User)
27// return &User{
28// Name: u.Name,
29// Balance: u.Balance,
30// Internal: "", // omit sensitive data
31// }
32// }
33//
34// // Create a read-only view of the tree
35// PublicTree := rotree.Wrap(tree, makeEntrySafeFn)
36//
37// // Safely access the data
38// value, _ := roTree.Get("alice")
39// user := value.(*User)
40// // user.Name == "Alice"
41// // user.Balance == 100
42// // user.Internal == "" (sensitive data is filtered)
43package rotree
44
45import (
46 "gno.land/p/nt/bptree/v0"
47)
48
49// Wrap creates a new ReadOnlyTree from an existing bptree.BPTree and a safety transformation function.
50// If makeEntrySafeFn is nil, values will be returned as-is without transformation.
51func Wrap(tree *bptree.BPTree, makeEntrySafeFn func(any) any) *ReadOnlyTree {
52 return &ReadOnlyTree{
53 tree: tree,
54 makeEntrySafeFn: makeEntrySafeFn,
55 }
56}
57
58// ReadOnlyTree wraps a bptree.BPTree and provides read-only access.
59type ReadOnlyTree struct {
60 tree *bptree.BPTree
61 makeEntrySafeFn func(any) any
62}
63
64// IReadOnlyTree defines the read-only operations available on a tree.
65type IReadOnlyTree interface {
66 Size() int
67 Has(key string) bool
68 Get(key string) (any, bool)
69 GetByIndex(index int) (string, any)
70 Iterate(start, end string, cb bptree.IterCbFn) bool
71 ReverseIterate(start, end string, cb bptree.IterCbFn) bool
72 IterateByOffset(offset int, count int, cb bptree.IterCbFn) bool
73 ReverseIterateByOffset(offset int, count int, cb bptree.IterCbFn) bool
74}
75
76// Verify that ReadOnlyTree implements both ITree and IReadOnlyTree
77var (
78 _ bptree.ITree = (*ReadOnlyTree)(nil)
79 _ IReadOnlyTree = (*ReadOnlyTree)(nil)
80)
81
82// getSafeValue applies the makeEntrySafeFn if it exists, otherwise returns the original value
83func (roTree *ReadOnlyTree) getSafeValue(value any) any {
84 if roTree.makeEntrySafeFn == nil {
85 return value
86 }
87 return roTree.makeEntrySafeFn(value)
88}
89
90// Size returns the number of key-value pairs in the tree.
91func (roTree *ReadOnlyTree) Size() int {
92 return roTree.tree.Size()
93}
94
95// Has checks whether a key exists in the tree.
96func (roTree *ReadOnlyTree) Has(key string) bool {
97 return roTree.tree.Has(key)
98}
99
100// Get retrieves the value associated with the given key, converted to a safe format.
101func (roTree *ReadOnlyTree) Get(key string) (any, bool) {
102 value, exists := roTree.tree.Get(key)
103 if !exists {
104 return nil, false
105 }
106 return roTree.getSafeValue(value), true
107}
108
109// GetByIndex retrieves the key-value pair at the specified index in the tree, with the value converted to a safe format.
110func (roTree *ReadOnlyTree) GetByIndex(index int) (string, any) {
111 key, value := roTree.tree.GetByIndex(index)
112 return key, roTree.getSafeValue(value)
113}
114
115// Iterate performs an in-order traversal of the tree within the specified key range.
116func (roTree *ReadOnlyTree) Iterate(start, end string, cb bptree.IterCbFn) bool {
117 return roTree.tree.Iterate(start, end, func(key string, value any) bool {
118 return cb(key, roTree.getSafeValue(value))
119 })
120}
121
122// ReverseIterate performs a reverse in-order traversal of the tree within the specified key range.
123func (roTree *ReadOnlyTree) ReverseIterate(start, end string, cb bptree.IterCbFn) bool {
124 return roTree.tree.ReverseIterate(start, end, func(key string, value any) bool {
125 return cb(key, roTree.getSafeValue(value))
126 })
127}
128
129// IterateByOffset performs an in-order traversal of the tree starting from the specified offset.
130func (roTree *ReadOnlyTree) IterateByOffset(offset int, count int, cb bptree.IterCbFn) bool {
131 return roTree.tree.IterateByOffset(offset, count, func(key string, value any) bool {
132 return cb(key, roTree.getSafeValue(value))
133 })
134}
135
136// ReverseIterateByOffset performs a reverse in-order traversal of the tree starting from the specified offset.
137func (roTree *ReadOnlyTree) ReverseIterateByOffset(offset int, count int, cb bptree.IterCbFn) bool {
138 return roTree.tree.ReverseIterateByOffset(offset, count, func(key string, value any) bool {
139 return cb(key, roTree.getSafeValue(value))
140 })
141}
142
143// Set is not supported on ReadOnlyTree and will panic.
144func (roTree *ReadOnlyTree) Set(key string, value any) bool {
145 panic("Set operation not supported on ReadOnlyTree")
146}
147
148// Remove is not supported on ReadOnlyTree and will panic.
149func (roTree *ReadOnlyTree) Remove(key string) (value any, removed bool) {
150 panic("Remove operation not supported on ReadOnlyTree")
151}