// Copyright Earl Warren // Copyright Loïc Dachary // 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 }