Skip to content

Commit

Permalink
feat(nav): implement directory sampling (#373)
Browse files Browse the repository at this point in the history
test(nav): add failing tests for sampling with filtering (#373)

ref(nav): prepare for stashing (#373)

ref(nav): stash files navigator (#373)

ref(nav): stash folder navigator (#373)

ref(nav): stash universal navigator (#373)

test(nav): tidy up numeric assertions using BecauseQuanity (#373)

ref(nav): remove navi from ascend/descend (#373)

feat(nav): add initial version of samplingBroker (#373)

ref(nav): correct naming of directory entries and contents (#373)

ref(nav): add broker helpers (#373)

ref(nav): add sampling helpers (#373)

feat(nav): implement initial filtered sampling for folders (#373)

feat(nav): intermediate check-in (#373)

feat(nav): implement filtered sampling for files and folders (#373)

feat(nav): implement initial version of directory entry while iterator (#373)

feat(collections): forward iterator (#373)

feat(collections): use collections.iterator (#373)

ref(nav): change broker and brokerage functionality (#373)

feat(nav): define directory entry while iterator (#373)

feat(nav): use directory entry while iterator (#373)

feat(nav): use sampling iterator driver (#373)

feat(nav): remove existing sampling functionality (redesign required) (#373)

feat(nav): rename sampling helpers to adapters (#373)

feat(nav): implementing reworking of slice sampling with controller (#373)

feat(nav): implementing reworking of filter sampling with controller (#373)

feat(nav): begin implementing reworking of universal filter sampling with controller (#373)

feat(nav): add file/folder to node scope (#373)

feat(nav): implement reworking of universal sampling with controller (#373)

feat(nav): implement custom sampling (#373)

feat(nav): move cache to agent (#373)

feat(nav): make agent consult cache before creting item (#373)

feat(nav): add key method to traverse item for caching (#373)

feat(nav): create new dual sampling adapter, use for universal (#373)

feat(nav): fix! new dual sampling adapter, use for file/folders (#373)

feat(nav): tidy up sampling code (#373)
  • Loading branch information
plastikfan committed Nov 29, 2023
1 parent 566d8c7 commit bc38f37
Show file tree
Hide file tree
Showing 37 changed files with 2,172 additions and 440 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cSpell.words": [
"Aphex",
"argh",
"Assisable",
"avfs",
Expand All @@ -17,9 +18,11 @@
"deepcopy",
"depguard",
"dogsled",
"doin",
"dotenv",
"dupl",
"ensync",
"Ephidrina",
"errcheck",
"exportloopref",
"extendio",
Expand Down Expand Up @@ -49,6 +52,7 @@
"graffico",
"incpatch",
"ineffassign",
"infex",
"Innerworld",
"isroot",
"jroot",
Expand All @@ -75,6 +79,7 @@
"onsi",
"outdir",
"prealloc",
"Ptrs",
"rabbitweed",
"repath",
"repotoken",
Expand Down
187 changes: 187 additions & 0 deletions collections/iterators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package collections

// IteratorCtrl represents a narrow view of the iterator that exposes
// just the method, Valid which indicates when all items in the
// sequence have been consumed. The purpose of this is to allow a
// client abstraction, which implements the looping logic to provide
// a condition which halts its iteration. There are 2 scenarios:
// 1) the client wants to iterate the entire sequence; in this case
// the client just needs to continue the iteration until Valid
// returns false.
// 2) the client only wants to iterate the sequence until a certain
// other condition arises; in this case, the client combines the
// result of Valid() with another predicate within the for statement.
type IteratorCtrl[T any] interface {
Valid() bool
}

// Iterator represents an iterator over a slice. The underlying slice can
// not be empty. When created, the iterator does not point to a valid
// slice entry. To begin iteration, the client invokes Start. At any one
// after the iteration has started, the iterator, points to the current item
// in the sequence. The client can query the validity of the current item
// using the Valid method. To obtain successive elements, the client invokes
// the Next method and this can be continued until Valid returns false. Once
// Valid returns false, the client should no longer call Next. Doing so in
// this scenario, returns the zero value for the value T.
// It should also be noted that the iterator does not in itself implement
// the loop operation, it merely provides the logic to enable the client
// to implement the looping operation.
type Iterator[T any] interface {
IteratorCtrl[T]

// Start returns the first element of the sequence and moves the current
// position to the next item.
Start() T

// Next moves the iterator to the next item in the sequence then returns
// that item. If the iterator is already at the end, then the zero
// value of T is returned. However, when used properly, this situation
// should never occur, as Valid would indicate that iteration with
// Next should no longer occur.
Next() T

// Reset is designed to be used in high frequency applications. The client
// can reuse this entry for a new collection rather that having throw this
// instance away and create a new one. This helps to reduce the number of
// allocations in a high frequency application.
Reset(entries []T)
}

// BeginIt creates a forward iterator over a non empty slice. If the provided
// slice is empty, then a nil iterator is returned. (NB: please remember to check
// for a nil interface correctly; see the helper function IsNil in utils).
//
// The zero value represents the value that is returned if the Next method on the
// iterator is incorrectly invoked after Valid has returned false.
// If the collection contains interfaces, or pointers just pass in nil as the
// zero value.
//
// If the collection contains scalars, pass in 0 cast to the appropriate type,
// eg int32(0). It doesn't matter if 0 is a valid value in the collection,
// because this value is only ever return in an invalid scenario, ie, calling
// Next after Valid has returned false. This is preferable than generating a
// panic. If the collection contains structs, then pass in an empty struct
// as the nil value.
func BeginIt[T any](elements []T, zero T) Iterator[T] {
// 📚 NB: it is not possible to obtain the type of a generic parameter at runtime
// using reflection. Generics in Go are a compile-time feature, and type information
// is generally not available at runtime due to the language's design principles.
// This is why we need the client to pass in the zero value manually.
//
return &forwardIterator[T]{
baseIterator: baseIterator[T]{
zero: zero,
container: elements,
current: -1,
},
}
}

// ReverseIt creates a reverse iterator over a non empty slice. If the provided
// slice is empty, then a nil iterator is returned. (NB: please remember to check
// for a nil interface correctly; see the helper function IsNil in utils).
func ReverseIt[T any](elements []T, zero T) Iterator[T] {
return &reverseIterator[T]{
baseIterator: baseIterator[T]{
zero: zero,
container: elements,
current: len(elements),
},
}
}

type baseIterator[T any] struct {
zero T
container []T
current int
}

// Valid returns true if the current position of the iterator points
// to a valid entry in the sequence. When the iterator reaches the
// end of the sequence, Valid returns false.
func (i *baseIterator[T]) Valid() bool {
return i.current >= 0 && i.current < len(i.container)
}

// forwardIterator navigates the sequence from the start (index 0) to the
// end (index len-1)
type forwardIterator[T any] struct {
baseIterator[T]
}

// Start returns the first element of the sequence and moves the current
// position to the next item.
func (i *forwardIterator[T]) Start() T {
if len(i.container) == 0 {
return i.zero
}

const initial = 0
i.current = initial

return i.container[i.current]
}

// Next moves the iterator to the next item in the sequence then returns
// that item. If the iterator is already at the end, then the zero
// value of T is returned. However, when used properly, this situation
// should never occur, as Valid would indicate that iteration with
// Next should no longer occur.
func (i *forwardIterator[T]) Next() T {
i.current++
if !i.Valid() {
return i.zero
}

return i.container[i.current]
}

// Reset is designed to be used in high frequency applications. The client
// can reuse this iterator for a new collection rather that having throw this
// instance away and create a new one. This helps to reduce the number of
// allocations in a high frequency application.
func (i *forwardIterator[T]) Reset(entries []T) {
i.container = entries
i.current = -1
}

type reverseIterator[T any] struct {
baseIterator[T]
}

// Start returns the last element of the sequence and moves the current
// position to the next item.
func (i *reverseIterator[T]) Start() T {
if len(i.container) == 0 {
return i.zero
}

const offset = 1
i.current = len(i.container) - offset

return i.container[i.current]
}

// Next moves the iterator to the next item in the sequence then returns
// that item. If the iterator is already at the end, then the zero
// value of T is returned. However, when used properly, this situation
// should never occur, as Valid would indicate that iteration with
// Next should no longer occur.
func (i *reverseIterator[T]) Next() T {
i.current--
if !i.Valid() {
return i.zero
}

return i.container[i.current]
}

// Reset is designed to be used in high frequency applications. The client
// can reuse this iterator for a new collection rather that having throw this
// instance away and create a new one. This helps to reduce the number of
// allocations in a high frequency application.
func (i *reverseIterator[T]) Reset(entries []T) {
i.container = entries
i.current = len(i.container)
}
Loading

0 comments on commit bc38f37

Please sign in to comment.