98 lines
2.5 KiB
Go
98 lines
2.5 KiB
Go
|
|
package loader
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"maps"
|
||
|
|
"slices"
|
||
|
|
"strings"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
ld "sourcery.dny.nu/longdistance"
|
||
|
|
"sourcery.dny.nu/pana/internal/json"
|
||
|
|
"sourcery.dny.nu/pana/vocab/geojson"
|
||
|
|
gts "sourcery.dny.nu/pana/vocab/gotosocial"
|
||
|
|
"sourcery.dny.nu/pana/vocab/litepub"
|
||
|
|
as "sourcery.dny.nu/pana/vocab/w3/activitystreams"
|
||
|
|
credv1 "sourcery.dny.nu/pana/vocab/w3id/credentialsv1"
|
||
|
|
idv1 "sourcery.dny.nu/pana/vocab/w3id/identityv1"
|
||
|
|
secv1 "sourcery.dny.nu/pana/vocab/w3id/securityv1"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Builtin is a remote context cache.
|
||
|
|
type Builtin struct {
|
||
|
|
domains map[string]ld.Document
|
||
|
|
paths map[string]ld.Document
|
||
|
|
pathKeys []string
|
||
|
|
|
||
|
|
lock sync.RWMutex
|
||
|
|
}
|
||
|
|
|
||
|
|
func New() *Builtin {
|
||
|
|
b := &Builtin{
|
||
|
|
domains: make(map[string]ld.Document, 8),
|
||
|
|
paths: make(map[string]ld.Document, 2),
|
||
|
|
}
|
||
|
|
b.RegisterContextURL(as.IRI, as.ContextDocument)
|
||
|
|
b.RegisterContextURL(gts.IRI, gts.ContextDocument)
|
||
|
|
b.RegisterContextURL(credv1.IRI, credv1.ContextDocument)
|
||
|
|
b.RegisterContextURL(idv1.IRI, idv1.ContextDocument)
|
||
|
|
b.RegisterContextURL(secv1.IRI, secv1.ContextDocument)
|
||
|
|
b.RegisterContextURL(litepub.IRI, litepub.ContextDocument)
|
||
|
|
b.RegisterContextURL(geojson.IRI, geojson.ContextDocument)
|
||
|
|
b.RegisterContextPath("/litepub-0.1.jsonld", litepub.ContextDocument1dot0)
|
||
|
|
return b
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get is an [ld.RemoteContextLoaderFunc].
|
||
|
|
func (b *Builtin) Get(_ context.Context, url string) (ld.Document, error) {
|
||
|
|
b.lock.RLock()
|
||
|
|
defer b.lock.RUnlock()
|
||
|
|
|
||
|
|
if doc, ok := b.domains[url]; ok {
|
||
|
|
return doc, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, path := range b.pathKeys {
|
||
|
|
if strings.HasSuffix(url, path) {
|
||
|
|
doc := b.paths[path]
|
||
|
|
doc.URL = url
|
||
|
|
return doc, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return ld.Document{}, ld.ErrLoadingRemoteContext
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterContextURL adds or overrides a context document for the specified
|
||
|
|
// remote context URL in the loader.
|
||
|
|
func (b *Builtin) RegisterContextURL(url string, doc json.RawMessage) error {
|
||
|
|
b.lock.Lock()
|
||
|
|
defer b.lock.Unlock()
|
||
|
|
|
||
|
|
ctx, err := json.GetContextDocument(doc)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
b.domains[url] = ld.Document{URL: url, Context: ctx}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterContextPath adds or overrides a context document for the specified
|
||
|
|
// remote path in the loader.
|
||
|
|
//
|
||
|
|
// Paths are always matches as URL suffixes.
|
||
|
|
func (b *Builtin) RegisterContextPath(path string, doc json.RawMessage) error {
|
||
|
|
b.lock.Lock()
|
||
|
|
defer b.lock.Unlock()
|
||
|
|
|
||
|
|
ctx, err := json.GetContextDocument(doc)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
b.paths[path] = ld.Document{URL: "", Context: ctx}
|
||
|
|
b.pathKeys = slices.Collect(maps.Keys(b.paths))
|
||
|
|
return nil
|
||
|
|
}
|