summaryrefslogtreecommitdiff
path: root/internal/tree
diff options
context:
space:
mode:
authorNeonXP <i@neonxp.dev>2022-11-21 03:46:30 +0300
committerNeonXP <i@neonxp.dev>2022-11-21 03:46:30 +0300
commit340a623e1a35efe0182cadd780a5a3385b526705 (patch)
tree7baad78e20b922b5487fa243c68b5b6137c7e1c4 /internal/tree
initial
Diffstat (limited to 'internal/tree')
-rw-r--r--internal/tree/engine.go42
-rw-r--r--internal/tree/mutations.go60
2 files changed, 102 insertions, 0 deletions
diff --git a/internal/tree/engine.go b/internal/tree/engine.go
new file mode 100644
index 0000000..0f28eb7
--- /dev/null
+++ b/internal/tree/engine.go
@@ -0,0 +1,42 @@
+package tree
+
+import (
+ "context"
+ "sync"
+
+ "go.neonxp.dev/djson/internal/storage"
+ "go.neonxp.dev/json/model"
+)
+
+type Engine struct {
+ Root model.Node
+ mu sync.RWMutex
+ storage storage.Storage
+}
+
+func New(storage storage.Storage) *Engine {
+ return &Engine{
+ Root: model.Node{},
+ mu: sync.RWMutex{},
+ storage: storage,
+ }
+}
+
+func (t *Engine) Run(ctx context.Context) error {
+ // Load initial mutations
+ for m := range t.storage.Load() {
+ if err := t.execute(&m); err != nil {
+ return err
+ }
+ }
+
+ <-ctx.Done()
+ return nil
+}
+
+func (t *Engine) Get(nodes []string) (*model.Node, error) {
+ if len(nodes) == 0 {
+ return &t.Root, nil
+ }
+ return t.Root.Query(nodes)
+}
diff --git a/internal/tree/mutations.go b/internal/tree/mutations.go
new file mode 100644
index 0000000..ec80ebd
--- /dev/null
+++ b/internal/tree/mutations.go
@@ -0,0 +1,60 @@
+package tree
+
+import (
+ "context"
+ "fmt"
+
+ "go.neonxp.dev/djson/internal/model"
+)
+
+func (t *Engine) Mutation(ctx context.Context, mut *model.Mutation) error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if err := t.execute(mut); err != nil {
+ return err
+ }
+ return t.storage.Commit(ctx, *mut)
+}
+
+func (t *Engine) execute(mut *model.Mutation) error {
+ switch mut.Type {
+ case model.Create:
+ if len(mut.Path) == 0 {
+ // create root node
+ t.Root = mut.Body
+ return nil
+ }
+ key := mut.Path[len(mut.Path)-1]
+ path := mut.Path[:len(mut.Path)-1]
+ target, err := t.Root.Query(path)
+ if err != nil {
+ return err
+ }
+ return target.Set(key, mut.Body)
+ case model.Merge:
+ if len(mut.Path) == 0 {
+ // patch root node
+ return t.Root.Merge(&mut.Body)
+ }
+ target, err := t.Root.Query(mut.Path)
+ if err != nil {
+ return err
+ }
+ return target.Merge(&mut.Body)
+ case model.Remove:
+ if len(mut.Path) == 0 {
+ return fmt.Errorf("can't remove root node. Only replace or create avaliable")
+ }
+ key := mut.Path[len(mut.Path)-1]
+ if len(mut.Path) == 1 {
+ return t.Root.Remove(key)
+ }
+ path := mut.Path[:len(mut.Path)-1]
+ target, err := t.Root.Query(path)
+ if err != nil {
+ return err
+ }
+ return target.Remove(key)
+ }
+ return fmt.Errorf("invalid command type: %d", mut.Type)
+}