rolist.gno

3.52 Kb ยท 127 lines
  1// Package rolist provides a read-only wrapper for list.List with safe value transformation.
  2//
  3// It is useful when you want to expose a read-only view of a list 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 list
 16//	var privateList list.List
 17//	privateList.Append(&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 list
 35//	publicList := rolist.Wrap(privateList, makeEntrySafeFn)
 36//
 37//	// Safely access the data
 38//	value := publicList.Get(0)
 39//	user := value.(*User)
 40//	// user.Name == "Alice"
 41//	// user.Balance == 100
 42//	// user.Internal == "" (sensitive data is filtered)
 43package rolist
 44
 45import (
 46	"gno.land/p/nt/avl/list"
 47	"gno.land/p/nt/avl/rotree"
 48)
 49
 50// IReadOnlyList defines the read-only operations available on a list.
 51type IReadOnlyList interface {
 52	Len() int
 53	Get(index int) any
 54	Slice(startIndex, endIndex int) []any
 55	ForEach(fn func(index int, value any) bool)
 56}
 57
 58// ReadOnlyList wraps a list.List and provides read-only access.
 59type ReadOnlyList struct {
 60	list            *list.List
 61	makeEntrySafeFn func(any) any
 62}
 63
 64// Verify interface implementations
 65var (
 66	_ IReadOnlyList = (*ReadOnlyList)(nil)
 67	_ IReadOnlyList = (interface{ list.IList })(nil) // is subset of list.IList
 68)
 69
 70// Wrap creates a new ReadOnlyList from an existing list.List and a safety transformation function.
 71// If makeEntrySafeFn is nil, values will be returned as-is without transformation.
 72func Wrap(list *list.List, makeEntrySafeFn func(any) any) *ReadOnlyList {
 73	return &ReadOnlyList{
 74		list:            list,
 75		makeEntrySafeFn: makeEntrySafeFn,
 76	}
 77}
 78
 79// getSafeValue applies the makeEntrySafeFn if it exists, otherwise returns the original value
 80func (rol *ReadOnlyList) getSafeValue(value any) any {
 81	if rol.makeEntrySafeFn == nil {
 82		return value
 83	}
 84	return rol.makeEntrySafeFn(value)
 85}
 86
 87// Len returns the number of elements in the list.
 88func (rol *ReadOnlyList) Len() int {
 89	return rol.list.Len()
 90}
 91
 92// Get returns the value at the specified index, converted to a safe format.
 93// Returns nil if index is out of bounds.
 94func (rol *ReadOnlyList) Get(index int) any {
 95	value := rol.list.Get(index)
 96	if value == nil {
 97		return nil
 98	}
 99	return rol.getSafeValue(value)
100}
101
102// Slice returns a slice of values from startIndex (inclusive) to endIndex (exclusive),
103// with all values converted to a safe format.
104func (rol *ReadOnlyList) Slice(startIndex, endIndex int) []any {
105	values := rol.list.Slice(startIndex, endIndex)
106	if values == nil {
107		return nil
108	}
109
110	result := make([]any, len(values))
111	for i, v := range values {
112		result[i] = rol.getSafeValue(v)
113	}
114	return result
115}
116
117// ForEach iterates through all elements in the list, providing safe versions of the values.
118func (rol *ReadOnlyList) ForEach(fn func(index int, value any) bool) {
119	rol.list.ForEach(func(index int, value any) bool {
120		return fn(index, rol.getSafeValue(value))
121	})
122}
123
124// Tree returns a read-only pointer to the underlying AVL tree.
125func (rol *ReadOnlyList) Tree() *rotree.ReadOnlyTree {
126	return rol.list.Tree()
127}