1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 13:30:26 +00:00
mycorrhiza/hyphae/backlinks/backlinks.go

159 lines
4.1 KiB
Go
Raw Normal View History

// Package backlinks maintains the index of backlinks and lets you update it and query it.
package backlinks
import (
2022-02-19 08:26:38 +00:00
"log"
"os"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/util"
)
// YieldHyphaBacklinks gets backlinks for the desired hypha, sorts and yields them one by one.
func YieldHyphaBacklinks(hyphaName string) <-chan string {
hyphaName = util.CanonicalName(hyphaName)
out := make(chan string)
sorted := hyphae.PathographicSort(out)
go func() {
backlinks, exists := backlinkIndex[hyphaName]
if exists {
for link := range backlinks {
out <- link
}
}
close(out)
}()
return sorted
}
var backlinkConveyor = make(chan backlinkIndexOperation) // No need to buffer because these operations are rare.
// RunBacklinksConveyor runs an index operation processing loop. Call it somewhere in main.
func RunBacklinksConveyor() {
// It is supposed to run as a goroutine for all the time. So, don't blame the infinite loop.
defer close(backlinkConveyor)
for {
(<-backlinkConveyor).apply()
}
}
var backlinkIndex = make(map[string]linkSet)
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index. Call it when indexing and reindexing hyphae.
func IndexBacklinks() {
// It is safe to ignore the mutex, because there is only one worker.
for h := range hyphae.FilterHyphaeWithText(hyphae.YieldExistingHyphae()) {
foundLinks := extractHyphaLinksFromContent(h.CanonicalName(), fetchText(h))
for _, link := range foundLinks {
if _, exists := backlinkIndex[link]; !exists {
backlinkIndex[link] = make(linkSet)
}
backlinkIndex[link][h.CanonicalName()] = struct{}{}
}
}
}
// BacklinksCount returns the amount of backlinks to the hypha.
2022-02-19 08:26:38 +00:00
func BacklinksCount(h hyphae.Hypha) int {
2022-02-03 22:29:01 +00:00
if links, exists := backlinkIndex[h.CanonicalName()]; exists {
return len(links)
}
return 0
}
// Using set here seems like the most appropriate solution
type linkSet map[string]struct{}
2021-08-31 19:28:12 +00:00
func toLinkSet(xs []string) linkSet {
result := make(linkSet)
for _, x := range xs {
result[x] = struct{}{}
}
return result
}
2022-02-19 08:26:38 +00:00
func fetchText(h hyphae.Hypha) string {
var path string
switch h := h.(type) {
case *hyphae.EmptyHypha:
2021-08-31 19:28:12 +00:00
return ""
2022-02-19 08:26:38 +00:00
case *hyphae.TextualHypha:
path = h.TextFilePath()
case *hyphae.MediaHypha:
if !h.HasTextFile() {
return ""
}
path = h.TextFilePath()
2021-08-31 19:28:12 +00:00
}
2022-02-19 08:26:38 +00:00
text, err := os.ReadFile(path)
if err != nil {
log.Println(err)
return ""
2021-08-31 19:28:12 +00:00
}
2022-02-19 08:26:38 +00:00
return string(text)
2021-08-31 19:28:12 +00:00
}
2021-12-20 20:59:23 +00:00
// backlinkIndexOperation is an operation for the backlink index. This operation is executed async-safe.
type backlinkIndexOperation interface {
apply()
2021-09-01 06:28:21 +00:00
}
2021-12-20 20:59:23 +00:00
// backlinkIndexEdit contains data for backlink index update after a hypha edit
type backlinkIndexEdit struct {
name string
oldLinks []string
newLinks []string
2021-09-01 06:28:21 +00:00
}
// apply changes backlink index respective to the operation data
2021-12-20 20:59:23 +00:00
func (op backlinkIndexEdit) apply() {
oldLinks := toLinkSet(op.oldLinks)
newLinks := toLinkSet(op.newLinks)
2021-09-01 06:28:21 +00:00
for link := range oldLinks {
if _, exists := newLinks[link]; !exists {
delete(backlinkIndex[link], op.name)
2021-09-01 06:28:21 +00:00
}
}
for link := range newLinks {
if _, exists := oldLinks[link]; !exists {
if _, exists := backlinkIndex[link]; !exists {
backlinkIndex[link] = make(linkSet)
}
backlinkIndex[link][op.name] = struct{}{}
2021-09-01 06:28:21 +00:00
}
}
}
2021-12-20 20:59:23 +00:00
// backlinkIndexDeletion contains data for backlink index update after a hypha deletion
type backlinkIndexDeletion struct {
name string
links []string
2021-09-01 06:28:21 +00:00
}
// apply changes backlink index respective to the operation data
2021-12-20 20:59:23 +00:00
func (op backlinkIndexDeletion) apply() {
for _, link := range op.links {
2021-09-01 06:28:21 +00:00
if lSet, exists := backlinkIndex[link]; exists {
delete(lSet, op.name)
2021-09-01 06:28:21 +00:00
}
}
}
2021-12-20 20:59:23 +00:00
// backlinkIndexRenaming contains data for backlink index update after a hypha renaming
type backlinkIndexRenaming struct {
oldName string
newName string
links []string
2021-09-01 06:28:21 +00:00
}
// Apply changes backlink index respective to the operation data
2021-12-20 20:59:23 +00:00
func (op backlinkIndexRenaming) apply() {
for _, link := range op.links {
2021-09-01 06:28:21 +00:00
if lSet, exists := backlinkIndex[link]; exists {
delete(lSet, op.oldName)
backlinkIndex[link][op.newName] = struct{}{}
2021-09-01 06:28:21 +00:00
}
}
}