93 lines
3.2 KiB
Go
93 lines
3.2 KiB
Go
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||
|
// SPDX-License-Identifier: MIT
|
||
|
|
||
|
package generic
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"strings"
|
||
|
|
||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||
|
"code.forgejo.org/f3/gof3/v3/path"
|
||
|
"code.forgejo.org/f3/gof3/v3/util"
|
||
|
)
|
||
|
|
||
|
type ErrorRemapReferencesRelative error
|
||
|
|
||
|
func RemapReferences(ctx context.Context, node NodeInterface, f f3.Interface) {
|
||
|
for _, reference := range f.GetReferences() {
|
||
|
toPath := path.NewPath()
|
||
|
collectTo := func(ctx context.Context, parent, p path.Path, node NodeInterface) {
|
||
|
element := NewNode()
|
||
|
mappedID := node.GetMappedID()
|
||
|
if mappedID.String() == "" {
|
||
|
node.Trace("mapped ID for %s is not defined", p.ReadableString())
|
||
|
}
|
||
|
element.SetID(mappedID)
|
||
|
toPath = toPath.Append(element.(path.PathElement))
|
||
|
}
|
||
|
from := reference.Get()
|
||
|
isRelative := !strings.HasPrefix(from, "/")
|
||
|
if isRelative && !strings.HasPrefix(from, "..") {
|
||
|
panic(NewError[ErrorRemapReferencesRelative]("relative references that do not start with .. are not supported '%s'", from))
|
||
|
}
|
||
|
current := node.GetCurrentPath().String()
|
||
|
fromPath := path.PathAbsolute(NewElementNode, current, from)
|
||
|
node.GetTree().Apply(ctx, fromPath, NewApplyOptions(collectTo).SetWhere(ApplyEachNode))
|
||
|
to := toPath.String()
|
||
|
node.Trace("from '%s' to '%s'", fromPath.ReadableString(), toPath.ReadableString())
|
||
|
if isRelative {
|
||
|
currentMapped := node.GetParent().GetCurrentPath().PathMappedString().Join()
|
||
|
// because the mapped ID of the current node has not been allocated yet
|
||
|
// and it does not matter as long as it is replaced with ..
|
||
|
// it will not work at all if a relative reference does not start with ..
|
||
|
currentMapped += "/PLACEHODLER"
|
||
|
to = path.PathRelativeString(currentMapped, to)
|
||
|
}
|
||
|
node.Trace("convert reference %s => %s", reference.Get(), to)
|
||
|
reference.Set(to)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NodeCollectReferences(ctx context.Context, node NodeInterface) []path.Path {
|
||
|
pathToReferences := make(map[string]path.Path, 5)
|
||
|
|
||
|
tree := node.GetTree()
|
||
|
|
||
|
collect := func(ctx context.Context, parent path.Path, node NodeInterface) {
|
||
|
util.MaybeTerminate(ctx)
|
||
|
f := node.GetSelf().ToFormat()
|
||
|
for _, reference := range f.GetReferences() {
|
||
|
absoluteReference := path.PathAbsoluteString(node.GetCurrentPath().String(), reference.Get())
|
||
|
if _, ok := pathToReferences[absoluteReference]; ok {
|
||
|
continue
|
||
|
}
|
||
|
tree.ApplyAndGet(ctx, path.NewPathFromString(NewElementNode, absoluteReference), NewApplyOptions(func(ctx context.Context, parent, path path.Path, node NodeInterface) {
|
||
|
pathToReferences[absoluteReference] = node.GetCurrentPath()
|
||
|
}))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
node.Walk(ctx, path.NewPath(node.(path.PathElement)), NewWalkOptions(collect))
|
||
|
|
||
|
references := make([]path.Path, 0, len(pathToReferences))
|
||
|
|
||
|
for _, reference := range pathToReferences {
|
||
|
tree.Debug("collect %s", reference)
|
||
|
references = append(references, reference)
|
||
|
}
|
||
|
|
||
|
return references
|
||
|
}
|
||
|
|
||
|
func TreeCollectReferences(ctx context.Context, tree TreeInterface, p path.Path) []path.Path {
|
||
|
var references []path.Path
|
||
|
|
||
|
tree.Apply(ctx, p, NewApplyOptions(func(ctx context.Context, parent, path path.Path, node NodeInterface) {
|
||
|
references = NodeCollectReferences(ctx, node)
|
||
|
}))
|
||
|
|
||
|
return references
|
||
|
}
|