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