2022-05-22 09:25:22 +00:00
|
|
|
|
// Package interwiki provides interwiki capabilities. Most of them, at least.
|
|
|
|
|
package interwiki
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
2022-06-21 21:39:56 +00:00
|
|
|
|
"errors"
|
2022-08-21 20:52:10 +00:00
|
|
|
|
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
|
2022-05-22 09:25:22 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/files"
|
2022-06-14 19:26:13 +00:00
|
|
|
|
"github.com/bouncepaw/mycorrhiza/util"
|
2022-05-22 09:25:22 +00:00
|
|
|
|
"log"
|
|
|
|
|
"os"
|
2022-06-21 21:39:56 +00:00
|
|
|
|
"sync"
|
2022-05-22 09:25:22 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func Init() {
|
|
|
|
|
var (
|
|
|
|
|
record, err = readInterwiki()
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
}
|
|
|
|
|
for _, wiki := range record {
|
2022-05-27 13:43:40 +00:00
|
|
|
|
wiki := wiki // This line is required
|
2022-05-22 09:25:22 +00:00
|
|
|
|
wiki.canonize()
|
2022-06-21 21:39:56 +00:00
|
|
|
|
if err := addEntry(&wiki); err != nil {
|
|
|
|
|
log.Fatalln(err.Error())
|
2022-05-22 09:25:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-21 21:39:56 +00:00
|
|
|
|
log.Printf("Loaded %d interwiki entries\n", len(listOfEntries))
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 19:37:14 +00:00
|
|
|
|
func dropEmptyStrings(ss []string) (clean []string) {
|
|
|
|
|
for _, s := range ss {
|
|
|
|
|
if s != "" {
|
|
|
|
|
clean = append(clean, s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return clean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// difference returns the elements in `a` that aren't in `b`.
|
|
|
|
|
// Taken from https://stackoverflow.com/a/45428032
|
|
|
|
|
// CC BY-SA 4.0, no changes made
|
|
|
|
|
func difference(a, b []string) []string {
|
|
|
|
|
mb := make(map[string]struct{}, len(b))
|
|
|
|
|
for _, x := range b {
|
|
|
|
|
mb[x] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
var diff []string
|
|
|
|
|
for _, x := range a {
|
|
|
|
|
if _, found := mb[x]; !found {
|
|
|
|
|
diff = append(diff, x)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return diff
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-21 21:39:56 +00:00
|
|
|
|
func areNamesFree(names []string) (bool, string) {
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
if _, found := entriesByName[name]; found {
|
|
|
|
|
return false, name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var mutex sync.Mutex
|
|
|
|
|
|
2023-08-07 19:37:14 +00:00
|
|
|
|
func replaceEntry(oldWiki *Wiki, newWiki *Wiki) error {
|
|
|
|
|
diff := difference(
|
|
|
|
|
append(newWiki.Aliases, newWiki.Name),
|
|
|
|
|
append(oldWiki.Aliases, oldWiki.Name),
|
|
|
|
|
)
|
|
|
|
|
if ok, name := areNamesFree(diff); !ok {
|
|
|
|
|
return errors.New(name)
|
|
|
|
|
}
|
|
|
|
|
deleteEntry(oldWiki)
|
|
|
|
|
return addEntry(newWiki)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func deleteEntry(wiki *Wiki) {
|
2022-06-21 21:39:56 +00:00
|
|
|
|
mutex.Lock()
|
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
2023-08-07 19:37:14 +00:00
|
|
|
|
// I'm being fancy here. Come on, the code here is already a mess.
|
|
|
|
|
// Let me have some fun.
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
|
|
wg.Add(2)
|
|
|
|
|
go func() {
|
|
|
|
|
names := append(wiki.Aliases, wiki.Name)
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
name := name // I guess we need that
|
|
|
|
|
delete(entriesByName, name)
|
|
|
|
|
}
|
|
|
|
|
wg.Done()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
for i, w := range listOfEntries {
|
|
|
|
|
i, w := i, w
|
|
|
|
|
if w.Name == wiki.Name {
|
|
|
|
|
log.Println("It came to delete")
|
|
|
|
|
// Drop ith element.
|
|
|
|
|
listOfEntries[i] = listOfEntries[len(listOfEntries)-1]
|
|
|
|
|
listOfEntries = listOfEntries[:len(listOfEntries)-1]
|
|
|
|
|
break
|
2022-07-11 08:51:58 +00:00
|
|
|
|
}
|
2023-08-07 19:37:14 +00:00
|
|
|
|
}
|
|
|
|
|
wg.Done()
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addEntry(wiki *Wiki) error {
|
|
|
|
|
mutex.Lock()
|
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
wiki.Aliases = dropEmptyStrings(wiki.Aliases)
|
|
|
|
|
var (
|
|
|
|
|
names = append(wiki.Aliases, wiki.Name)
|
2022-06-21 21:39:56 +00:00
|
|
|
|
ok, name = areNamesFree(names)
|
|
|
|
|
)
|
|
|
|
|
if !ok {
|
|
|
|
|
log.Printf("There are multiple uses of the same name ‘%s’\n", name)
|
|
|
|
|
return errors.New(name)
|
|
|
|
|
}
|
2022-07-11 08:51:58 +00:00
|
|
|
|
if len(names) == 0 {
|
|
|
|
|
log.Println("No names passed for a new interwiki entry")
|
|
|
|
|
// There is something clearly wrong with error-returning in this function.
|
|
|
|
|
return errors.New("")
|
|
|
|
|
}
|
2022-06-21 21:39:56 +00:00
|
|
|
|
|
|
|
|
|
listOfEntries = append(listOfEntries, wiki)
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
entriesByName[name] = wiki
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2022-05-22 09:25:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-04 19:55:00 +00:00
|
|
|
|
func HrefLinkFormatFor(prefix string) (string, options.InterwikiError) {
|
2022-06-14 19:26:13 +00:00
|
|
|
|
prefix = util.CanonicalName(prefix)
|
2022-06-21 21:39:56 +00:00
|
|
|
|
if wiki, ok := entriesByName[prefix]; ok {
|
2022-06-04 19:55:00 +00:00
|
|
|
|
return wiki.LinkHrefFormat, options.Ok
|
2022-05-28 17:05:22 +00:00
|
|
|
|
}
|
2022-06-04 19:55:00 +00:00
|
|
|
|
return "", options.UnknownPrefix
|
2022-05-28 17:19:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-04 19:55:00 +00:00
|
|
|
|
func ImgSrcFormatFor(prefix string) (string, options.InterwikiError) {
|
2022-06-14 19:26:13 +00:00
|
|
|
|
prefix = util.CanonicalName(prefix)
|
2022-06-21 21:39:56 +00:00
|
|
|
|
if wiki, ok := entriesByName[prefix]; ok {
|
2022-06-04 19:55:00 +00:00
|
|
|
|
return wiki.ImgSrcFormat, options.Ok
|
2022-05-28 17:19:40 +00:00
|
|
|
|
}
|
2022-06-04 19:55:00 +00:00
|
|
|
|
return "", options.UnknownPrefix
|
2022-05-28 17:05:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-22 09:25:22 +00:00
|
|
|
|
func readInterwiki() ([]Wiki, error) {
|
|
|
|
|
var (
|
|
|
|
|
record []Wiki
|
|
|
|
|
fileContents, err = os.ReadFile(files.InterwikiJSON())
|
|
|
|
|
)
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return record, nil
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(fileContents, &record)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return record, nil
|
|
|
|
|
}
|
2022-06-21 21:39:56 +00:00
|
|
|
|
|
|
|
|
|
func saveInterwikiJson() {
|
|
|
|
|
// Trust me, wiki crashing when an admin takes an administrative action totally makes sense.
|
|
|
|
|
if data, err := json.MarshalIndent(listOfEntries, "", "\t"); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
} else if err = os.WriteFile(files.InterwikiJSON(), data, 0666); err != nil {
|
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
} else {
|
|
|
|
|
log.Println("Saved interwiki.json")
|
|
|
|
|
}
|
|
|
|
|
}
|