mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-10-30 11:46:16 +00:00
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
package hyphae
|
|
|
|
// File `iterators.go` contains stuff that iterates over hyphae.
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var byNames = make(map[string]ExistingHypha)
|
|
var byNamesMutex = sync.Mutex{}
|
|
|
|
// YieldExistingHyphae iterates over all hyphae and yields all existing ones.
|
|
func YieldExistingHyphae() chan ExistingHypha {
|
|
ch := make(chan ExistingHypha)
|
|
go func() {
|
|
for _, h := range byNames {
|
|
switch h.(type) {
|
|
default:
|
|
ch <- h
|
|
}
|
|
}
|
|
close(ch)
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
// FilterHyphaeWithText filters the source channel and yields only those hyphae than have text parts.
|
|
func FilterHyphaeWithText(src chan ExistingHypha) chan ExistingHypha {
|
|
// TODO: reimplement as a function with a callback?
|
|
sink := make(chan ExistingHypha)
|
|
go func() {
|
|
for h := range src {
|
|
switch h := h.(type) {
|
|
case *TextualHypha:
|
|
sink <- h
|
|
case *MediaHypha:
|
|
if h.HasTextFile() {
|
|
sink <- h
|
|
}
|
|
}
|
|
}
|
|
close(sink)
|
|
}()
|
|
return sink
|
|
}
|
|
|
|
// PathographicSort sorts paths inside the source channel, preserving the path tree structure
|
|
func PathographicSort(src chan string) <-chan string {
|
|
out := make(chan string)
|
|
go func() {
|
|
// To make it unicode-friendly and lean, we cast every string into rune slices, sort, and only then cast them back
|
|
raw := make([][]rune, 0)
|
|
for h := range src {
|
|
raw = append(raw, []rune(h))
|
|
}
|
|
sort.Slice(raw, func(i, j int) bool {
|
|
const slash rune = 47 // == '/'
|
|
// Classic lexicographical sort with a twist
|
|
c := 0
|
|
for {
|
|
if c == len(raw[i]) {
|
|
return true
|
|
}
|
|
if c == len(raw[j]) {
|
|
return false
|
|
}
|
|
if raw[i][c] == raw[j][c] {
|
|
c++
|
|
} else {
|
|
// The twist: subhyphae-awareness is about pushing slash upwards
|
|
if raw[i][c] == slash {
|
|
return true
|
|
}
|
|
if raw[j][c] == slash {
|
|
return false
|
|
}
|
|
return raw[i][c] < raw[j][c]
|
|
}
|
|
}
|
|
})
|
|
for _, name := range raw {
|
|
out <- string(name)
|
|
}
|
|
close(out)
|
|
}()
|
|
return out
|
|
}
|
|
|
|
// Subhyphae returns slice of subhyphae.
|
|
func Subhyphae(h Hypha) []ExistingHypha {
|
|
var hyphae []ExistingHypha
|
|
for subh := range YieldExistingHyphae() {
|
|
if strings.HasPrefix(subh.CanonicalName(), h.CanonicalName()+"/") {
|
|
hyphae = append(hyphae, subh)
|
|
}
|
|
}
|
|
return hyphae
|
|
}
|
|
|
|
// AreFreeNames checks if all given `hyphaNames` are not taken. If they are not taken, `ok` is true. If not, `firstFailure` is the name of the first met hypha that is not free.
|
|
func AreFreeNames(hyphaNames ...string) (firstFailure string, ok bool) {
|
|
for h := range YieldExistingHyphae() {
|
|
for _, hn := range hyphaNames {
|
|
if hn == h.CanonicalName() {
|
|
return hn, false
|
|
}
|
|
}
|
|
}
|
|
return "", true
|
|
}
|