/
sampling-while-iterator.go
215 lines (180 loc) Β· 5.71 KB
/
sampling-while-iterator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package nav
import (
"io/fs"
"path/filepath"
"github.com/samber/lo"
"github.com/snivilised/extendio/collections"
)
// WhileDirectoryPredicate determines when to terminate the loop
type WhileDirectoryPredicate func(fi *FilteredInfo) bool
// EachDirectoryItemPredicate callback to invoke for each child item
type EachDirectoryItemPredicate func(childItem *TraverseItem) bool
// EnoughAlready when using the universal navigator within a sampling operation, set
// these accordingly from inside the while predicate to indicate wether the iteration
// loop should continue to consider more entries to be included in the sample. So
// set Files/Folders flags to true, when enough of those items have been included.
type EnoughAlready struct {
Files bool
Folders bool
}
// FilteredInfo used within the sampling process during a traversal; more specifically,
// they should be set inside the while predicate. Note, the Enough field is only
// appropriate when using the universal navigator.
type FilteredInfo struct {
Counts EntryQuantities
Enough EnoughAlready
}
// directoryEntryWhileIt
type directoryEntryWhileIt struct {
o *TraverseOptions
forward bool
each EachDirectoryItemPredicate
while WhileDirectoryPredicate
zero fs.DirEntry
adapters samplingAdaptersCollection
navigator inspector
iterator collections.Iterator[fs.DirEntry]
tp *traverseParams
universal bool
}
type newDirectoryEntryWhileItParams struct {
o *TraverseOptions
useCustomSampler bool
filter TraverseFilter
adapters samplingAdaptersCollection
forward bool
each EachDirectoryItemPredicate
while WhileDirectoryPredicate
}
// newDirectoryEntryWhileIt represents the predefined while iterator, which
// uses the defined filter to create a sample of the directory entries. A
// client can if they wish define their own custom while iterator that
// does not use a filter. The while iterator that is returned implements
// the looping mechanism used to sample directory entries. It uses a while
// and each predicate to control the iteration process and select items
// into the sample set.
//
// To define the behaviour of the directory while iterator, the client needs to
// provide their own each and while predicates and set them in options at
// Store.Samplers.Custom;
// - each predicate; this is the function that is invoked for each
// child TraverseItem to be considered for the sample.
// - while predicate; a function which controls how long the loop will
// continue to run. When the predicate returns false, then sampling stops
// for the current directory.
func newDirectoryEntryWhileIt(
params *newDirectoryEntryWhileItParams,
) *directoryEntryWhileIt {
var (
subscription = params.o.Store.Subscription
whit = &directoryEntryWhileIt{
o: params.o,
forward: params.forward,
adapters: params.adapters,
universal: subscription == SubscribeAny,
}
)
if params.useCustomSampler {
whit.each = params.each
whit.while = params.while
} else {
filter := params.filter
whit.each = func(childItem *TraverseItem) bool {
return filter.IsMatch(childItem)
}
noOf := params.o.Store.Sampling.NoOf
whit.while = func(fi *FilteredInfo) bool {
return whit.iterator.Valid() && (!whit.adapters[subscription].isFull(fi, &noOf))
}
}
return whit
}
func (i *directoryEntryWhileIt) isNil() bool {
// π It is ok to use == operator with nil interface here because
// the iterator interface has not been set to anything so the value of the
// interface itself is nil as well as the value it points to.
//
return i.iterator == nil
}
func (i *directoryEntryWhileIt) initInspector(navigator inspector) {
i.navigator = navigator
}
func (i *directoryEntryWhileIt) withParams(tp *traverseParams) {
i.tp = tp
}
func (i *directoryEntryWhileIt) start(entries []fs.DirEntry) {
if i.isNil() {
i.iterator = lo.TernaryF(i.forward,
func() collections.Iterator[fs.DirEntry] {
return collections.ForwardIt[fs.DirEntry](entries, i.zero)
},
func() collections.Iterator[fs.DirEntry] {
return collections.ReverseIt[fs.DirEntry](entries, i.zero)
},
)
} else {
i.iterator.Reset(entries)
}
}
// sample creates a sample with a new collection of directories entries
func (i *directoryEntryWhileIt) sample(entries []fs.DirEntry, processingFiles bool) []fs.DirEntry {
i.start(entries)
result := i.enumerate()
return lo.Ternary[[]fs.DirEntry](processingFiles, result.Files, result.Folders)
}
func (i *directoryEntryWhileIt) samples(
sourceEntries *DirectoryContents,
) (files, folders []fs.DirEntry) {
i.start(sourceEntries.All())
result := i.enumerate()
files = result.Files
folders = result.Folders
return
}
func (i *directoryEntryWhileIt) enumerate() *DirectoryContents {
result := newEmptyDirectoryEntries(i.o, &i.o.Store.Sampling.NoOf)
parent := i.tp.current
var fi FilteredInfo
for entry := i.iterator.Start(); i.while(&fi); entry = i.iterator.Next() {
if entry == nil {
break
}
info, err := entry.Info()
if i.universal {
if fi.Enough.Files && !info.IsDir() {
break
}
if fi.Enough.Folders && info.IsDir() {
break
}
}
path := filepath.Join(parent.Path, entry.Name())
child := newTraverseItem(
path,
entry,
info,
parent,
err,
)
stash := i.navigator.inspect(&traverseParams{ // preview
current: child,
frame: i.tp.frame,
navi: &NavigationInfo{
Options: i.tp.navi.Options,
Item: child,
frame: i.tp.frame,
},
})
if i.each(child) {
i.navigator.keep(stash)
if entry.IsDir() {
result.Folders = append(result.Folders, entry)
fi.Counts.Folders++
} else {
result.Files = append(result.Files, entry)
fi.Counts.Files++
}
}
}
return result
}