350 lines
7.9 KiB
Go
350 lines
7.9 KiB
Go
// Copyright Earl Warren <contact@earl-warren.org>
|
|
// Copyright Loïc Dachary <loic@dachary.org>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package memory
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"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/options"
|
|
options_logger "code.forgejo.org/f3/gof3/v3/options/logger"
|
|
"code.forgejo.org/f3/gof3/v3/path"
|
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
|
)
|
|
|
|
type IDAllocatorInterface interface {
|
|
allocateID(id string) string
|
|
isNull() bool
|
|
}
|
|
|
|
type idAllocatorGenerator struct {
|
|
prefix string
|
|
lastID rune
|
|
}
|
|
|
|
func NewIDAllocatorGenerator(prefix string) IDAllocatorInterface {
|
|
return &idAllocatorGenerator{
|
|
prefix: prefix,
|
|
lastID: 'A',
|
|
}
|
|
}
|
|
|
|
func (o *idAllocatorGenerator) allocateID(string) string {
|
|
r := fmt.Sprintf("%s-%c", o.prefix, o.lastID)
|
|
o.lastID++
|
|
return r
|
|
}
|
|
|
|
func (o *idAllocatorGenerator) isNull() bool { return false }
|
|
|
|
type idAllocatorNull struct{}
|
|
|
|
func (o *idAllocatorNull) allocateID(id string) string { return id }
|
|
|
|
func (o *idAllocatorNull) isNull() bool { return true }
|
|
|
|
func NewIDAllocatorNull() IDAllocatorInterface {
|
|
return &idAllocatorNull{}
|
|
}
|
|
|
|
type memoryOptions struct {
|
|
options.Options
|
|
options_logger.OptionsLogger
|
|
|
|
IDAllocator IDAllocatorInterface
|
|
}
|
|
|
|
func NewOptions(idAllocator IDAllocatorInterface) options.Interface {
|
|
opts := &memoryOptions{}
|
|
opts.IDAllocator = idAllocator
|
|
opts.SetName("memory")
|
|
l := logger.NewLogger()
|
|
l.SetLevel(logger.Trace)
|
|
opts.SetLogger(l)
|
|
return opts
|
|
}
|
|
|
|
type memoryStorage struct {
|
|
idAllocator IDAllocatorInterface
|
|
root *memoryStorageNode
|
|
}
|
|
|
|
func newmemoryStorage(opts options.Interface) *memoryStorage {
|
|
return &memoryStorage{
|
|
idAllocator: opts.(*memoryOptions).IDAllocator,
|
|
}
|
|
}
|
|
|
|
func (o *memoryStorage) newStorageNode(id string) *memoryStorageNode {
|
|
return &memoryStorageNode{
|
|
storage: o,
|
|
f: NewFormat(o.idAllocator.allocateID(id)),
|
|
children: make(map[string]*memoryStorageNode),
|
|
}
|
|
}
|
|
|
|
func (o *memoryStorage) Find(path path.PathString) *memoryStorageNode {
|
|
p := path.Elements()
|
|
fmt.Printf("memoryStorage Find %s\n", path.Join())
|
|
current := o.root
|
|
for {
|
|
fmt.Printf(" memoryStorage Find lookup '%s'\n", current.f.GetID())
|
|
if current.f.GetID() != p[0] {
|
|
panic("")
|
|
}
|
|
p = p[1:]
|
|
if len(p) == 0 {
|
|
return current
|
|
}
|
|
if next, ok := current.children[p[0]]; ok {
|
|
current = next
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
type memoryStorageNode struct {
|
|
storage *memoryStorage
|
|
f *FormatMemory
|
|
children map[string]*memoryStorageNode
|
|
}
|
|
|
|
type treeDriver struct {
|
|
generic.NullTreeDriver
|
|
storage *memoryStorage
|
|
allocateID bool
|
|
}
|
|
|
|
func newTreeDriver(opts options.Interface) *treeDriver {
|
|
tree := &treeDriver{
|
|
storage: newmemoryStorage(opts),
|
|
allocateID: !opts.(*memoryOptions).IDAllocator.isNull(),
|
|
}
|
|
tree.Init()
|
|
return tree
|
|
}
|
|
|
|
func (o *treeDriver) AllocateID() bool { return o.allocateID }
|
|
|
|
func (o *treeDriver) Factory(ctx context.Context, kind kind.Kind) generic.NodeDriverInterface {
|
|
return &Driver{}
|
|
}
|
|
|
|
type Driver struct {
|
|
generic.NullDriver
|
|
f *FormatMemory
|
|
}
|
|
|
|
type FormatMemory struct {
|
|
f3.Common
|
|
Ref *f3.Reference
|
|
Content string
|
|
}
|
|
|
|
func NewFormat(id string) *FormatMemory {
|
|
f := &FormatMemory{}
|
|
f.Ref = f3.NewReference("")
|
|
f.Content = "????"
|
|
f.SetID(id)
|
|
return f
|
|
}
|
|
|
|
func (o *FormatMemory) GetReferences() f3.References {
|
|
references := o.Common.GetReferences()
|
|
if o.Ref.Get() != "" {
|
|
references = append(references, o.Ref)
|
|
}
|
|
return references
|
|
}
|
|
|
|
func (o *Driver) NewFormat() f3.Interface {
|
|
return &FormatMemory{}
|
|
}
|
|
|
|
func (o *Driver) ToFormat() f3.Interface {
|
|
if o == nil || o.f == nil {
|
|
return o.NewFormat()
|
|
}
|
|
return &FormatMemory{
|
|
Common: o.f.Common,
|
|
Ref: o.f.Ref,
|
|
Content: o.f.Content,
|
|
}
|
|
}
|
|
|
|
func (o *Driver) FromFormat(f f3.Interface) {
|
|
m := f.(*FormatMemory)
|
|
o.f = &FormatMemory{
|
|
Common: m.Common,
|
|
Ref: m.Ref,
|
|
Content: m.Content,
|
|
}
|
|
}
|
|
|
|
func (o *Driver) Equals(ctx context.Context, other generic.NodeInterface) bool {
|
|
switch i := other.GetDriver().(type) {
|
|
case *Driver:
|
|
return o.f.Content == i.f.Content
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (o *Driver) String() string {
|
|
if o.f == nil {
|
|
return "<nil>"
|
|
}
|
|
return o.f.Content
|
|
}
|
|
|
|
func (o *Driver) Get(ctx context.Context) bool {
|
|
node := o.GetNode()
|
|
o.Trace("id '%s' '%s'", node.GetID(), node.GetCurrentPath().ReadableString())
|
|
storage := o.getStorage(node, node.GetCurrentPath())
|
|
if storage != nil {
|
|
o.f = storage.f
|
|
}
|
|
node.List(ctx)
|
|
return true
|
|
}
|
|
|
|
func (o *Driver) Put(ctx context.Context) id.NodeID {
|
|
return o.upsert(ctx)
|
|
}
|
|
|
|
func (o *Driver) Patch(ctx context.Context) {
|
|
o.upsert(ctx)
|
|
}
|
|
|
|
func (o *Driver) upsert(ctx context.Context) id.NodeID {
|
|
node := o.GetNode()
|
|
path := node.GetParent().GetCurrentPath()
|
|
storageParent := o.getStorage(node, path)
|
|
i := node.GetID()
|
|
o.Trace("node id '%s'", i)
|
|
if existing, ok := storageParent.children[i.String()]; ok {
|
|
o.Trace("update %s content=%s ref=%s", node.GetCurrentPath().ReadableString(), o.f.Content, o.f.Ref)
|
|
existing.f = o.f
|
|
} else {
|
|
storageChild := storageParent.storage.newStorageNode(i.String())
|
|
storageID := storageChild.f.GetID()
|
|
storageParent.children[storageID] = storageChild
|
|
i = id.NewNodeID(storageID)
|
|
if o.f == nil {
|
|
o.f = storageChild.f
|
|
} else {
|
|
o.f.SetID(storageChild.f.GetID())
|
|
}
|
|
o.Trace("create '%s' '%s'", node.GetCurrentPath().ReadableString(), i)
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (o *Driver) Delete(ctx context.Context) {
|
|
node := o.GetNode()
|
|
storage := o.getStorage(node, node.GetParent().GetCurrentPath())
|
|
if storage != nil && storage.children != nil {
|
|
id := node.GetID().String()
|
|
delete(storage.children, id)
|
|
}
|
|
}
|
|
|
|
func (o *Driver) getStorage(node generic.NodeInterface, path path.Path) *memoryStorageNode {
|
|
storage := node.GetTree().GetDriver().(*treeDriver).storage
|
|
return storage.Find(path.PathString())
|
|
}
|
|
|
|
func (o *Driver) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
|
node := o.GetNode()
|
|
o.Trace("'%s'", node.GetCurrentPath().ReadableString())
|
|
storage := o.getStorage(node, node.GetCurrentPath())
|
|
|
|
children := generic.NewChildrenSlice(0)
|
|
if storage != nil {
|
|
for i := range storage.children {
|
|
node := node.CreateChild(context.Background())
|
|
childID := id.NewNodeID(i)
|
|
node.SetID(childID)
|
|
children = append(children, node)
|
|
}
|
|
}
|
|
return children
|
|
}
|
|
|
|
func (o *Driver) GetIDFromName(ctx context.Context, content string) id.NodeID {
|
|
node := o.GetNode()
|
|
o.Trace("'%s'", node.GetCurrentPath().ReadableString())
|
|
storage := o.getStorage(node, node.GetCurrentPath())
|
|
|
|
if storage != nil {
|
|
for i, child := range storage.children {
|
|
if child.f.Content == content {
|
|
o.Trace("found '%s'", i)
|
|
return id.NewNodeID(i)
|
|
}
|
|
}
|
|
}
|
|
return id.NilID
|
|
}
|
|
|
|
type treeMemory struct {
|
|
generic.Tree
|
|
}
|
|
|
|
func newTreeMemory(ctx context.Context, opts options.Interface) generic.TreeInterface {
|
|
t := &treeMemory{}
|
|
t.Init(t, opts)
|
|
t.GetLogger().SetLevel(logger.Trace)
|
|
t.Register(kind.KindNil, func(ctx context.Context, kind kind.Kind) generic.NodeInterface {
|
|
return generic.NewNode()
|
|
})
|
|
|
|
treeDriver := newTreeDriver(opts)
|
|
t.SetDriver(treeDriver)
|
|
|
|
f := NewFormat("")
|
|
f.Content = "ROOT"
|
|
|
|
storage := treeDriver.storage
|
|
storage.root = &memoryStorageNode{
|
|
storage: storage,
|
|
f: f,
|
|
children: make(map[string]*memoryStorageNode),
|
|
}
|
|
root := t.Factory(ctx, kind.KindRoot)
|
|
root.FromFormat(f)
|
|
t.SetRoot(root)
|
|
|
|
return t
|
|
}
|
|
|
|
func SetContent(node generic.NodeInterface, content string) {
|
|
f := node.GetDriver().ToFormat().(*FormatMemory)
|
|
f.Content = content
|
|
node.FromFormat(f)
|
|
}
|
|
|
|
func GetContent(node generic.NodeInterface) string {
|
|
return node.GetDriver().ToFormat().(*FormatMemory).Content
|
|
}
|
|
|
|
func SetRef(node generic.NodeInterface, ref string) {
|
|
f := node.GetDriver().ToFormat().(*FormatMemory)
|
|
f.Ref = f3.NewReference(ref)
|
|
node.FromFormat(f)
|
|
}
|
|
|
|
func GetRef(node generic.NodeInterface) string {
|
|
return node.GetDriver().ToFormat().(*FormatMemory).Ref.Get()
|
|
}
|
|
|
|
func init() {
|
|
generic.RegisterFactory("memory", newTreeMemory)
|
|
}
|