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