commit 0ee0159b513122c43680c1e98a2838a322d32ba9
parent f322102e49b4c8f2a00679aee9428faf3c4e2241
Author: lash <dev@holbrook.no>
Date: Wed, 28 Aug 2024 02:42:39 +0100
Add gdbm persister
Diffstat:
14 files changed, 203 insertions(+), 28 deletions(-)
diff --git a/dev/gdbm/main.go b/dev/gdbm/main.go
@@ -12,7 +12,7 @@ import (
gdbm "github.com/graygnuorg/go-gdbm"
- "git.defalsify.org/vise.git/resource"
+ "git.defalsify.org/vise.git/db"
)
var (
@@ -45,7 +45,7 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
- typ = resource.FSRESOURCETYPE_UNKNOWN
+ typ = db.DATATYPE_UNKNOWN
if d.IsDir() {
return nil
}
@@ -53,9 +53,9 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error {
fb := path.Base(fp)
switch fx {
case binaryPrefix:
- typ = resource.FSRESOURCETYPE_BIN
+ typ = db.DATATYPE_BIN
case templatePrefix:
- typ = resource.FSRESOURCETYPE_TEMPLATE
+ typ = db.DATATYPE_TEMPLATE
default:
log.Printf("skip foreign file: %s", fp)
return nil
@@ -72,7 +72,7 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error {
log.Printf("fx fb %s %s", fx, fb)
ft := fb[:len(fb)-len(fx)]
- k := resource.ToDbKey(typ, ft, nil)
+ k := db.ToDbKey(typ, ft, nil)
err = sc.db.Store(k, v, true)
if err != nil {
return err
diff --git a/examples/gdbm/Makefile b/examples/gdbm/Makefile
@@ -0,0 +1,11 @@
+INPUTS = $(wildcard ./*.vis)
+TXTS = $(wildcard ./*.txt.orig)
+
+%.vis:
+ go run ../../dev/asm $(basename $@).vis > $(basename $@).bin
+ go run ../../dev/gdbm/main.go .
+
+all: $(INPUTS) $(TXTS)
+
+%.txt.orig:
+ cp -v $(basename $@).orig $(basename $@)
diff --git a/examples/gdbm/aiee.vis b/examples/gdbm/aiee.vis
@@ -0,0 +1,2 @@
+LOAD do 0
+HALT
diff --git a/examples/gdbm/main.go b/examples/gdbm/main.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path"
+
+ testdataloader "github.com/peteole/testdata-loader"
+
+ "git.defalsify.org/vise.git/cache"
+ "git.defalsify.org/vise.git/engine"
+ "git.defalsify.org/vise.git/resource"
+ "git.defalsify.org/vise.git/state"
+)
+
+var (
+ baseDir = testdataloader.GetBasePath()
+ scriptDir = path.Join(baseDir, "examples", "gdbm")
+ dbFile = path.Join(scriptDir, "vise.gdbm")
+)
+
+func do(ctx context.Context, sym string, input []byte) (resource.Result, error) {
+ return resource.Result{
+ Content: "bye",
+ }, nil
+}
+
+func main() {
+ var err error
+ root := "root"
+ fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, scriptDir)
+
+ st := state.NewState(0)
+ rs := resource.NewGdbmResource(dbFile)
+ ca := cache.NewCache()
+ if err != nil {
+ panic(err)
+ }
+ cfg := engine.Config{
+ Root: "root",
+ Language: "nor",
+ }
+ ctx := context.Background()
+ en := engine.NewEngine(ctx, cfg, &st, rs, ca)
+
+ rs.AddLocalFunc("do", do)
+
+ _, err = en.Init(ctx)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err)
+ os.Exit(1)
+ }
+ err = engine.Loop(ctx, &en, os.Stdin, os.Stdout)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/gdbm/menu b/examples/gdbm/menu
@@ -0,0 +1 @@
+welcome
+\ No newline at end of file
diff --git a/examples/gdbm/menu.vis b/examples/gdbm/menu.vis
@@ -0,0 +1,5 @@
+MOUT again 0
+MOUT quit 1
+HALT
+INCMP ^ 0
+INCMP aiee 1
diff --git a/examples/gdbm/quit_menu b/examples/gdbm/quit_menu
@@ -0,0 +1 @@
+or quit
+\ No newline at end of file
diff --git a/examples/gdbm/root b/examples/gdbm/root
@@ -0,0 +1 @@
+ready?
+\ No newline at end of file
diff --git a/examples/gdbm/root.vis b/examples/gdbm/root.vis
@@ -0,0 +1,2 @@
+HALT
+INCMP menu *
diff --git a/examples/gdbm/root_nor b/examples/gdbm/root_nor
@@ -0,0 +1 @@
+klar?
+\ No newline at end of file
diff --git a/persist/fs.go b/persist/fs.go
@@ -67,8 +67,12 @@ func(p *FsPersister) Save(key string) error {
return err
}
fp := path.Join(p.dir, key)
+ err = ioutil.WriteFile(fp, b, 0600)
+ if err != nil {
+ return err
+ }
Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags)
- return ioutil.WriteFile(fp, b, 0600)
+ return nil
}
// Load implements the Persister interface.
@@ -79,6 +83,9 @@ func(p *FsPersister) Load(key string) error {
return err
}
err = p.Deserialize(b)
+ if err != nil {
+ return err
+ }
Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code)
- return err
+ return nil
}
diff --git a/persist/gdbm.go b/persist/gdbm.go
@@ -0,0 +1,92 @@
+package persist
+
+import (
+ "github.com/fxamacker/cbor/v2"
+ gdbm "github.com/graygnuorg/go-gdbm"
+
+ "git.defalsify.org/vise.git/cache"
+ "git.defalsify.org/vise.git/state"
+ "git.defalsify.org/vise.git/db"
+)
+
+// gdbmPersister is an implementation of Persister that saves state to the file system.
+type gdbmPersister struct {
+ State *state.State
+ Memory *cache.Cache
+ db *gdbm.Database
+}
+
+func NewGdbmPersiser(fp string) *gdbmPersister {
+ gdb, err := gdbm.Open(fp, gdbm.ModeReader)
+ if err != nil {
+ panic(err)
+ }
+ return NewGdbmPersisterFromDatabase(gdb)
+}
+
+func NewGdbmPersisterFromDatabase(gdb *gdbm.Database) *gdbmPersister {
+ return &gdbmPersister{
+ db: gdb,
+ }
+}
+
+// WithContent sets a current State and Cache object.
+//
+// This method is normally called before Serialize / Save.
+func(p *gdbmPersister) WithContent(st *state.State, ca *cache.Cache) *gdbmPersister {
+ p.State = st
+ p.Memory = ca
+ return p
+}
+
+// TODO: DRY
+// GetState implements the Persister interface.
+func(p *gdbmPersister) GetState() *state.State {
+ return p.State
+}
+
+// GetMemory implements the Persister interface.
+func(p *gdbmPersister) GetMemory() cache.Memory {
+ return p.Memory
+}
+
+// Serialize implements the Persister interface.
+func(p *gdbmPersister) Serialize() ([]byte, error) {
+ return cbor.Marshal(p)
+}
+
+// Deserialize implements the Persister interface.
+func(p *gdbmPersister) Deserialize(b []byte) error {
+ err := cbor.Unmarshal(b, p)
+ return err
+}
+
+// Save implements the Persister interface.
+func(p *gdbmPersister) Save(key string) error {
+ b, err := p.Serialize()
+ if err != nil {
+ return err
+ }
+ k := db.ToDbKey(db.DATATYPE_STATE, key, nil)
+ err = p.db.Store(k, b, true)
+ if err != nil {
+ return err
+ }
+ Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags)
+ return nil
+}
+
+// Load implements the Persister interface.
+func(p *gdbmPersister) Load(key string) error {
+ k := db.ToDbKey(db.DATATYPE_STATE, key, nil)
+ b, err := p.db.Fetch(k)
+ if err != nil {
+ return err
+ }
+ err = p.Deserialize(b)
+ if err != nil {
+ return err
+ }
+ Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code)
+ return nil
+}
diff --git a/resource/fs.go b/resource/fs.go
@@ -13,12 +13,6 @@ import (
"git.defalsify.org/vise.git/lang"
)
-const (
- FSRESOURCETYPE_UNKNOWN = iota
- FSRESOURCETYPE_BIN
- FSRESOURCETYPE_TEMPLATE
-)
-
type FsResource struct {
MenuResource
Path string
diff --git a/resource/gdbm.go b/resource/gdbm.go
@@ -6,44 +6,39 @@ import (
gdbm "github.com/graygnuorg/go-gdbm"
"git.defalsify.org/vise.git/lang"
+ "git.defalsify.org/vise.git/db"
)
-
type gdbmResource struct {
db *gdbm.Database
fns map[string]EntryFunc
}
func NewGdbmResource(fp string) *gdbmResource {
- db, err := gdbm.Open(fp, gdbm.ModeReader)
+ gdb, err := gdbm.Open(fp, gdbm.ModeReader)
if err != nil {
panic(err)
}
- return &gdbmResource{
- db: db,
- }
+ return NewGdbmResourceFromDatabase(gdb)
}
-func ToDbKey(typ uint8, s string, l *lang.Language) []byte {
- k := []byte{typ}
- if l != nil && l.Code != "" {
- s += "_" + l.Code
+func NewGdbmResourceFromDatabase(gdb *gdbm.Database) *gdbmResource {
+ return &gdbmResource{
+ db: gdb,
}
- return append(k, []byte(s)...)
}
-
func(dbr *gdbmResource) GetTemplate(ctx context.Context, sym string) (string, error) {
var ln lang.Language
v := ctx.Value("Language")
if v != nil {
ln = v.(lang.Language)
}
- k := ToDbKey(FSRESOURCETYPE_TEMPLATE, sym, &ln)
+ k := db.ToDbKey(db.DATATYPE_TEMPLATE, sym, &ln)
r, err := dbr.db.Fetch(k)
if err != nil {
if err.(*gdbm.GdbmError).Is(gdbm.ErrItemNotFound) {
- k = ToDbKey(FSRESOURCETYPE_TEMPLATE, sym, nil)
+ k = db.ToDbKey(db.DATATYPE_TEMPLATE, sym, nil)
r, err = dbr.db.Fetch(k)
if err != nil {
return "", err
@@ -54,7 +49,7 @@ func(dbr *gdbmResource) GetTemplate(ctx context.Context, sym string) (string, er
}
func(dbr *gdbmResource) GetCode(sym string) ([]byte, error) {
- k := ToDbKey(FSRESOURCETYPE_BIN, sym, nil)
+ k := db.ToDbKey(db.DATATYPE_BIN, sym, nil)
return dbr.db.Fetch(k)
}
@@ -65,7 +60,7 @@ func(dbr *gdbmResource) GetMenu(ctx context.Context, sym string) (string, error)
if v != nil {
ln = v.(lang.Language)
}
- k := ToDbKey(FSRESOURCETYPE_TEMPLATE, msym, &ln)
+ k := db.ToDbKey(db.DATATYPE_TEMPLATE, msym, &ln)
r, err := dbr.db.Fetch(k)
if err != nil {
if err.(*gdbm.GdbmError).Is(gdbm.ErrItemNotFound) {