Search Apps Documentation Source Content File Folder Download Copy Actions Download

storage.gno

3.39 Kb · 141 lines
  1package boards
  2
  3import (
  4	"errors"
  5	"strings"
  6
  7	"gno.land/p/nt/avl/v0"
  8)
  9
 10type (
 11	// BoardIterFn defines a function type to iterate boards.
 12	BoardIterFn func(*Board) bool
 13
 14	// Storage defines an interface for boards storage.
 15	Storage interface {
 16		// Get retruns a boards that matches an ID.
 17		Get(ID) (_ *Board, found bool)
 18
 19		// GetByName retruns a boards that matches a name.
 20		GetByName(name string) (_ *Board, found bool)
 21
 22		// Remove removes a board from the storage.
 23		Remove(ID) (_ *Board, removed bool)
 24
 25		// Add adds a board to the storage.
 26		Add(*Board) error
 27
 28		// Size returns the number of boards in the storage.
 29		Size() int
 30
 31		// Iterate iterates boards.
 32		// To reverse iterate boards use a negative count.
 33		// If the callback returns true, the iteration is stopped.
 34		Iterate(start, count int, fn BoardIterFn) bool
 35	}
 36)
 37
 38// NewStorage creates a new boards storage.
 39func NewStorage() Storage {
 40	return &storage{
 41		byID:   avl.NewTree(),
 42		byName: avl.NewTree(),
 43	}
 44}
 45
 46type storage struct {
 47	byID   *avl.Tree // string(Board.ID) -> *Board
 48	byName *avl.Tree // Board.Name -> Board.ID
 49}
 50
 51// Get returns a board for a specific ID.
 52func (s storage) Get(boardID ID) (*Board, bool) {
 53	key := makeBoardKey(boardID)
 54	v, found := s.byID.Get(key)
 55	if !found {
 56		return nil, false
 57	}
 58	return v.(*Board), true
 59}
 60
 61// Get returns a board for a specific name.
 62func (s storage) GetByName(name string) (*Board, bool) {
 63	key := makeBoardNameKey(name)
 64	v, found := s.byName.Get(key)
 65	if !found {
 66		return nil, false
 67	}
 68	return s.Get(v.(ID))
 69}
 70
 71// Remove removes a board from the storage.
 72// It returns false when board is not found.
 73func (s *storage) Remove(boardID ID) (*Board, bool) {
 74	board, found := s.Get(boardID)
 75	if !found {
 76		return nil, false
 77	}
 78
 79	// Remove indexes for current and previous board names
 80	names := append([]string{board.Name}, board.Aliases...)
 81	for _, name := range names {
 82		key := makeBoardNameKey(name)
 83
 84		// Make sure that name is indexed to the board being removed
 85		v, found := s.byName.Get(key)
 86		if found && v.(ID) == boardID {
 87			s.byName.Remove(key)
 88		}
 89	}
 90
 91	key := makeBoardKey(board.ID)
 92	_, removed := s.byID.Remove(key)
 93	return board, removed
 94}
 95
 96// Add adds a board to the storage.
 97// If board already exists it updates storage by reindexing the board by ID and name.
 98// When board name changes it's indexed so it can be found with the new and previous names.
 99func (s *storage) Add(board *Board) error {
100	if board == nil {
101		return errors.New("adding nil boards to the storage is not allowed")
102	}
103
104	key := makeBoardKey(board.ID)
105	s.byID.Set(key, board)
106
107	// Index by name when the optional board name is not empty
108	if key = makeBoardNameKey(board.Name); key != "" {
109		s.byName.Set(key, board.ID)
110	}
111	return nil
112}
113
114// Size returns the number of boards in the storage.
115func (s storage) Size() int {
116	return s.byID.Size()
117}
118
119// Iterate iterates boards.
120// To reverse iterate boards use a negative count.
121// If the callback returns true, the iteration is stopped.
122func (s storage) Iterate(start, count int, fn BoardIterFn) bool {
123	if count < 0 {
124		return s.byID.ReverseIterateByOffset(start, -count, func(_ string, v any) bool {
125			return fn(v.(*Board))
126		})
127	}
128
129	return s.byID.IterateByOffset(start, count, func(_ string, v any) bool {
130		return fn(v.(*Board))
131	})
132}
133
134func makeBoardKey(boardID ID) string {
135	return boardID.Key()
136}
137
138func makeBoardNameKey(name string) string {
139	name = strings.TrimSpace(name)
140	return strings.ToLower(name)
141}