1
0
Fork 0
golang-forgejo-f3-gof3/tree/generic/node.go
Daniel Baumann 03bfe4079e
Adding upstream version 3.10.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 09:37:23 +02:00

414 lines
9.6 KiB
Go

// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"fmt"
"sort"
"code.forgejo.org/f3/gof3/v3/f3"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/logger"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/util"
)
type ChildrenSlice []NodeInterface
func NewChildrenSlice(len int) ChildrenSlice {
return make([]NodeInterface, 0, len)
}
func (o ChildrenSlice) Len() int {
return len(o)
}
func (o ChildrenSlice) Swap(i, j int) {
o[i], o[j] = o[j], o[i]
}
func (o ChildrenSlice) Less(i, j int) bool {
return o[i].GetID().String() < o[j].GetID().String()
}
type NodeChildren map[id.NodeID]NodeInterface
func NewNodeChildren() NodeChildren {
return make(NodeChildren)
}
var (
NilNode = &Node{isNil: true}
NilParent = NilNode
)
type Node struct {
logger.Logger
tree TreeInterface
isNil bool
sync bool
kind kind.Kind
id id.NodeID
driver NodeDriverInterface
self NodeInterface
parent NodeInterface
children NodeChildren
}
func NewElementNode() path.PathElement {
return NewNode().(path.PathElement)
}
func NewNode() NodeInterface {
node := &Node{}
return node.Init(node)
}
func NewNodeFromID[T any](i T) NodeInterface {
node := NewNode()
node.SetID(id.NewNodeID(i))
return node
}
func (o *Node) Init(self NodeInterface) NodeInterface {
o.SetTree(nil)
o.SetIsNil(true)
o.SetKind(kind.KindNil)
o.SetID(id.NilID)
o.SetSelf(self)
o.SetParent(NilNode)
o.children = NewNodeChildren()
return self
}
func (o *Node) GetIsNil() bool { return o == nil || o.isNil }
func (o *Node) SetIsNil(isNil bool) { o.isNil = isNil }
func (o *Node) GetIsSync() bool { return o.sync }
func (o *Node) SetIsSync(sync bool) { o.sync = sync }
func (o *Node) GetSelf() NodeInterface { return o.self }
func (o *Node) SetSelf(self NodeInterface) { o.self = self }
func (o *Node) GetParent() NodeInterface { return o.parent }
func (o *Node) SetParent(parent NodeInterface) { o.parent = parent }
func (o *Node) GetKind() kind.Kind { return o.kind }
func (o *Node) SetKind(kind kind.Kind) { o.kind = kind }
func (o *Node) GetID() id.NodeID {
if o.id == nil {
return id.NilID
}
return o.id
}
func (o *Node) SetID(id id.NodeID) { o.id = id }
func (o *Node) GetMappedID() id.NodeID {
mappedID := o.GetDriver().GetMappedID()
if mappedID == nil {
mappedID = id.NilID
}
return mappedID
}
func (o *Node) SetMappedID(mapped id.NodeID) {
o.GetDriver().SetMappedID(mapped)
}
func (o *Node) GetTree() TreeInterface { return o.tree }
func (o *Node) SetTree(tree TreeInterface) {
o.tree = tree
if tree != nil {
o.SetLogger(tree.GetLogger())
}
}
func (o *Node) GetDriver() NodeDriverInterface { return o.driver }
func (o *Node) SetDriver(driver NodeDriverInterface) {
driver.SetNode(o)
o.driver = driver
}
func (o *Node) GetNodeChildren() NodeChildren {
return o.children
}
func (o *Node) GetChildren() ChildrenSlice {
children := NewChildrenSlice(len(o.children))
for _, child := range o.children {
children = append(children, child)
}
sort.Sort(children)
return children
}
func (o *Node) SetChildren(children NodeChildren) { o.children = children }
func (o *Node) String() string {
driver := o.GetDriver().String()
if driver != "" {
driver = "=" + driver
}
return o.GetID().String() + driver
}
func (o *Node) Equals(ctx context.Context, other NodeInterface) bool {
return o.GetKind() == other.GetKind() && o.GetID() == other.GetID()
}
func (o *Node) GetCurrentPath() path.Path {
var p path.Path
if o.GetIsNil() {
p = path.NewPath()
} else {
p = o.GetParent().GetCurrentPath().Append(o)
}
return p
}
func (o *Node) ListPage(ctx context.Context, page int) ChildrenSlice {
return o.GetDriver().ListPage(ctx, page)
}
func (o *Node) List(ctx context.Context) ChildrenSlice {
o.Trace("%s", o.GetKind())
children := NewNodeChildren()
self := o.GetSelf()
for page := 1; ; page++ {
util.MaybeTerminate(ctx)
childrenPage := self.ListPage(ctx, page)
for _, child := range childrenPage {
children[child.GetID()] = child
}
if len(childrenPage) < o.GetTree().GetPageSize() {
break
}
}
o.children = children
return o.GetChildren()
}
func (o *Node) GetChild(id id.NodeID) NodeInterface {
child, ok := o.children[id]
if !ok {
return NilNode
}
return child
}
func (o *Node) SetChild(child NodeInterface) NodeInterface {
o.children[child.GetID()] = child
return child
}
func (o *Node) DeleteChild(id id.NodeID) NodeInterface {
if child, ok := o.children[id]; ok {
delete(o.children, id)
return child
}
return NilNode
}
func (o *Node) CreateChild(ctx context.Context) NodeInterface {
tree := o.GetTree()
child := tree.Factory(ctx, tree.GetChildrenKind(o.GetKind()))
child.SetParent(o)
return child
}
func (o *Node) GetIDFromName(ctx context.Context, name string) id.NodeID {
return o.GetDriver().GetIDFromName(ctx, name)
}
func (o *Node) Get(ctx context.Context) NodeInterface {
if o.GetDriver().Get(ctx) {
o.SetIsSync(true)
}
return o
}
func (o *Node) Upsert(ctx context.Context) NodeInterface {
if o.GetID() != id.NilID {
o.GetDriver().Patch(ctx)
} else {
o.SetID(o.GetDriver().Put(ctx))
}
if o.GetParent() != NilNode {
o.GetParent().SetChild(o)
}
return o
}
func (o *Node) Delete(ctx context.Context) NodeInterface {
o.GetDriver().Delete(ctx)
return o.GetParent().DeleteChild(o.GetID())
}
func (o *Node) NewFormat() f3.Interface {
return o.GetDriver().NewFormat()
}
func (o *Node) FromFormat(f f3.Interface) NodeInterface {
o.SetID(id.NewNodeID(f.GetID()))
o.GetDriver().FromFormat(f)
return o
}
func (o *Node) ToFormat() f3.Interface {
if o == nil || o.GetDriver() == nil {
return nil
}
return o.GetDriver().ToFormat()
}
func (o *Node) LookupMappedID(id id.NodeID) id.NodeID { return o.GetDriver().LookupMappedID(id) }
func (o *Node) ApplyAndGet(ctx context.Context, p path.Path, options *ApplyOptions) bool {
applyWrapInGet := func(options *ApplyOptions) ApplyFunc {
return func(ctx context.Context, parent, p path.Path, node NodeInterface) {
if options.fun != nil && (p.Empty() || options.where == ApplyEachNode) {
options.fun(ctx, parent, p, node)
}
if p.Empty() {
return
}
childID := p.First().(NodeInterface).GetID()
// is it a known child?
child := node.GetChild(childID)
// if not, maybe it is a known child referenced by name
if child.GetIsNil() {
childIDFromName := node.GetIDFromName(ctx, childID.String())
if childIDFromName != id.NilID {
childID = childIDFromName
child = node.GetChild(childID)
p.First().(NodeInterface).SetID(childID)
}
}
// not a known child by ID or by Name: make one
if child.GetIsNil() {
child = node.CreateChild(ctx)
child.SetID(childID)
if child.Get(ctx).GetIsSync() {
// only set the child if the driver is able to get it, otherwise
// it means that although the ID is known the child itself cannot be
// obtained and is assumed to be not found
node.SetChild(child)
}
}
}
}
return o.Apply(ctx, path.NewPath(), p, NewApplyOptions(applyWrapInGet(options)).SetWhere(ApplyEachNode))
}
func (o *Node) WalkAndGet(ctx context.Context, parent path.Path, options *WalkOptions) {
walkWrapInGet := func(fun WalkFunc) WalkFunc {
return func(ctx context.Context, p path.Path, node NodeInterface) {
node.Get(ctx)
node.List(ctx)
if fun != nil {
fun(ctx, p, node)
}
}
}
o.Walk(ctx, parent, NewWalkOptions(walkWrapInGet(options.fun)))
}
func (o *Node) Walk(ctx context.Context, parent path.Path, options *WalkOptions) {
util.MaybeTerminate(ctx)
options.fun(ctx, parent, o.GetSelf())
parent = parent.Append(o.GetSelf().(path.PathElement))
for _, child := range o.GetSelf().GetChildren() {
child.Walk(ctx, parent, options)
}
}
func (o *Node) FindAndGet(ctx context.Context, p path.Path) NodeInterface {
var r NodeInterface
r = NilNode
set := func(ctx context.Context, parent, p path.Path, node NodeInterface) {
r = node
}
o.ApplyAndGet(ctx, p, NewApplyOptions(set))
return r
}
func (o *Node) MustFind(p path.Path) NodeInterface {
found := o.Find(p)
if found.GetIsNil() {
panic(fmt.Errorf("%s not found", p))
}
return found
}
func (o *Node) Find(p path.Path) NodeInterface {
if p.Empty() {
return o
}
child := o.GetChild(p.First().(NodeInterface).GetID())
if child.GetIsNil() {
o.Info("'%s' not found", p.String())
return NilNode
}
return child.Find(p.RemoveFirst())
}
func (o *Node) Apply(ctx context.Context, parentPath, p path.Path, options *ApplyOptions) bool {
o.Trace("parent '%s', node '%s', path '%s'", parentPath.ReadableString(), o.String(), p.ReadableString())
util.MaybeTerminate(ctx)
if p.Empty() {
options.fun(ctx, parentPath, p, o.GetSelf())
return true
}
i := p.First().(NodeInterface).GetID().String()
if i == "." {
return o.Apply(ctx, parentPath, p.RemoveFirst(), options)
}
if i == ".." {
parent, parentPath := parentPath.Pop()
return parent.(NodeInterface).Apply(ctx, parentPath, p.RemoveFirst(), options)
}
if options.where == ApplyEachNode {
options.fun(ctx, parentPath, p, o.GetSelf())
}
child := o.GetChild(p.First().(NodeInterface).GetID())
if child.GetIsNil() && options.search == ApplySearchByName {
if childID := o.GetIDFromName(ctx, p.First().(NodeInterface).GetID().String()); childID != id.NilID {
child = o.GetChild(childID)
}
}
if child.GetIsNil() {
return false
}
parentPath = parentPath.Append(o.GetSelf().(path.PathElement))
return child.Apply(ctx, parentPath, p.RemoveFirst(), options)
}
type ErrorNodeNotFound error
func NewError[T error](message string, args ...any) T {
e := fmt.Errorf(message, args...)
return e.(T)
}