member_storage.gno
4.84 Kb · 174 lines
1package commondao
2
3import (
4 "errors"
5
6 "gno.land/p/moul/addrset"
7)
8
9type (
10 // MemberIterFn defines a callback to iterate DAO members.
11 MemberIterFn func(address) bool
12
13 // MemberStorage defines an interface for member storages.
14 MemberStorage interface {
15 // Size returns the number of members in the storage.
16 Size() int
17
18 // Has checks if a member exists in the storage.
19 Has(address) bool
20
21 // Add adds a member to the storage.
22 // Returns true if the member is added, or false if it already existed.
23 Add(address) bool
24
25 // Remove removes a member from the storage.
26 // Returns true if member was removed, or false if it was not found.
27 Remove(address) bool
28
29 // Grouping returns member groups when supported.
30 // When nil is returned it means that grouping of members is not supported.
31 // Member groups can be used by implementations that require grouping users
32 // by roles or by tiers for example.
33 Grouping() MemberGrouping
34
35 // IterateByOffset iterates members starting at the given offset.
36 // The callback can return true to stop iteration.
37 IterateByOffset(offset, count int, fn MemberIterFn) (stopped bool)
38 }
39)
40
41// NewMemberStorage creates a new member storage.
42// Function returns a new member storage that doesn't support member groups.
43// This type of storage is useful when there is no need to group members.
44func NewMemberStorage() MemberStorage {
45 return &memberStorage{}
46}
47
48// NewMemberStorageWithGrouping a new member storage with support for member groups.
49// Member groups can be used by implementations that require grouping users by roles
50// or by tiers for example.
51func NewMemberStorageWithGrouping(options ...MemberGroupingOption) MemberStorage {
52 return &memberStorage{grouping: NewMemberGrouping(options...)}
53}
54
55type memberStorage struct {
56 addrset.Set
57
58 grouping MemberGrouping
59}
60
61// Size returns the number of members in the storage.
62// The result is the number of members within the base underlying storage,
63// grouped members are not included, they must be counted separately.
64func (s memberStorage) Size() int {
65 return s.Set.Size()
66}
67
68// Has checks if a member exists in the storage.
69// Member is also searched within all defined member groups.
70func (s memberStorage) Has(member address) bool {
71 // Check underlying member address storage
72 if s.Set.Has(member) {
73 return true
74 }
75
76 if s.grouping == nil {
77 return false
78 }
79
80 // Check groups when member is not found in underlying storage
81 return s.grouping.IterateByOffset(0, s.grouping.Size(), func(g MemberGroup) bool {
82 return g.Members().Has(member)
83 })
84}
85
86// Grouping returns member groups.
87func (s memberStorage) Grouping() MemberGrouping {
88 return s.grouping
89}
90
91// IterateByOffset iterates members starting at the given offset.
92// The callback can return true to stop iteration.
93func (s memberStorage) IterateByOffset(offset, count int, fn MemberIterFn) bool {
94 var stopped bool
95 s.Set.IterateByOffset(offset, count, func(member address) bool {
96 stopped = fn(member)
97 return stopped
98 })
99 return stopped
100}
101
102// NewReadonlyMemberStorage creates a new readonly member storage.
103func NewReadonlyMemberStorage(s MemberStorage) (*ReadonlyMemberStorage, error) {
104 if s == nil {
105 return nil, errors.New("member storage is required")
106 }
107 return &ReadonlyMemberStorage{s}, nil
108}
109
110// MustNewReadonlyMemberStorage creates a new readonly member storage or panics on error.
111func MustNewReadonlyMemberStorage(s MemberStorage) *ReadonlyMemberStorage {
112 storage, err := NewReadonlyMemberStorage(s)
113 if err != nil {
114 panic(err)
115 }
116 return storage
117}
118
119// ReadonlyMemberStorage defines a readonly member storage.
120type ReadonlyMemberStorage struct {
121 storage MemberStorage
122}
123
124// Size returns the number of members in the storage.
125func (s ReadonlyMemberStorage) Size() int {
126 if s.storage == nil {
127 return 0
128 }
129 return s.storage.Size()
130}
131
132// Has checks if a member exists in the storage.
133func (s ReadonlyMemberStorage) Has(member address) bool {
134 if s.storage == nil {
135 return false
136 }
137 return s.storage.Has(member)
138}
139
140// Grouping returns member groups.
141func (s ReadonlyMemberStorage) Grouping() *ReadonlyMemberGrouping {
142 if s.storage == nil {
143 return nil
144 }
145
146 if g := s.storage.Grouping(); g != nil {
147 return MustNewReadonlyMemberGrouping(g)
148 }
149 return nil
150}
151
152// IterateByOffset iterates members starting at the given offset.
153// The callback can return true to stop iteration.
154func (s ReadonlyMemberStorage) IterateByOffset(offset, count int, fn MemberIterFn) bool {
155 if s.storage != nil {
156 return s.storage.IterateByOffset(offset, count, fn)
157 }
158 return false
159}
160
161// CountStorageMembers returns the total number of members in the storage.
162// It counts all members in each group and the ones without group.
163func CountStorageMembers(s *ReadonlyMemberStorage) int {
164 if s == nil {
165 return 0
166 }
167
168 c := s.Size()
169 s.Grouping().IterateByOffset(0, s.Grouping().Size(), func(g *ReadonlyMemberGroup) bool {
170 c += g.Members().Size()
171 return false
172 })
173 return c
174}