commit 0f5b1195d745e627f20adc6aa727b82fe8d54ee3
parent 6c3cb1731bb08eebdb29674c58a4530508dbb2b2
Author: lash <dev@holbrook.no>
Date: Fri, 30 Aug 2024 20:55:04 +0100
Implement db backend for resource
Diffstat:
6 files changed, 171 insertions(+), 13 deletions(-)
diff --git a/db/db.go b/db/db.go
@@ -8,11 +8,16 @@ import (
)
const (
- DATATYPE_UNKNOWN = iota
- DATATYPE_BIN
- DATATYPE_TEMPLATE
- DATATYPE_STATE
- DATATYPE_USERSTART
+ DATATYPE_UNKNOWN = 0
+ DATATYPE_BIN = 1
+ DATATYPE_MENU = 2
+ DATATYPE_TEMPLATE = 4
+ DATATYPE_STATE = 8
+ DATATYPE_USERSTART = 16
+)
+
+const (
+ datatype_sessioned_threshold = DATATYPE_TEMPLATE
)
// Db abstracts all data storage and retrieval as a key-value store
@@ -63,9 +68,14 @@ func(db *BaseDb) SetSession(sessionId string) {
// ToKey creates a DbKey within the current session context.
func(db *BaseDb) ToKey(key []byte) ([]byte, error) {
+ var b []byte
if db.pfx == DATATYPE_UNKNOWN {
return nil, errors.New("datatype prefix cannot be UNKNOWN")
}
- b := append(db.sid, key...)
+ if (db.pfx > datatype_sessioned_threshold) {
+ b = append(db.sid, key...)
+ } else {
+ b = key
+ }
return ToDbKey(db.pfx, b, nil), nil
}
diff --git a/resource/db.go b/resource/db.go
@@ -0,0 +1,68 @@
+package resource
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "git.defalsify.org/vise.git/db"
+)
+
+const (
+ resource_max_datatype = db.DATATYPE_TEMPLATE
+)
+
+type dbGetter struct {
+ typs uint8
+ db db.Db
+}
+
+func NewDbFuncGetter(store db.Db, typs... uint8) (*dbGetter, error) {
+ var v uint8
+ g := &dbGetter{
+ db: store,
+ }
+ for _, v = range(typs) {
+ if v > resource_max_datatype {
+ return nil, fmt.Errorf("datatype %d is not a resource", v)
+ }
+ g.typs |= v
+ }
+ return g, nil
+}
+
+func(g *dbGetter) fn(ctx context.Context, sym string) ([]byte, error) {
+ return g.db.Get(ctx, []byte(sym))
+}
+
+func(g *dbGetter) sfn(ctx context.Context, sym string) (string, error) {
+ b, err := g.fn(ctx, sym)
+ if err != nil {
+ return "", err
+ }
+ return string(b), nil
+}
+
+func(g *dbGetter) GetTemplate(ctx context.Context, sym string) (string, error) {
+ if g.typs & db.DATATYPE_TEMPLATE == 0{
+ return "", errors.New("not a template getter")
+ }
+ g.db.SetPrefix(db.DATATYPE_TEMPLATE)
+ return g.sfn(ctx, sym)
+}
+
+func(g *dbGetter) GetMenu(ctx context.Context, sym string) (string, error) {
+ if g.typs & db.DATATYPE_MENU == 0{
+ return "", errors.New("not a menu getter")
+ }
+ g.db.SetPrefix(db.DATATYPE_MENU)
+ return g.sfn(ctx, sym)
+}
+
+func(g *dbGetter) GetCode(ctx context.Context, sym string) ([]byte, error) {
+ if g.typs & db.DATATYPE_BIN == 0{
+ return nil, errors.New("not a code getter")
+ }
+ g.db.SetPrefix(db.DATATYPE_BIN)
+ return g.fn(ctx, sym)
+}
diff --git a/resource/db_test.go b/resource/db_test.go
@@ -0,0 +1,78 @@
+package resource
+
+import (
+ "bytes"
+ "context"
+ "testing"
+
+ "git.defalsify.org/vise.git/db"
+)
+
+func TestDb(t *testing.T) {
+ ctx := context.Background()
+ store := db.NewMemDb(ctx)
+ tg, err := NewDbFuncGetter(store, db.DATATYPE_TEMPLATE)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rs := NewMenuResource()
+ rs.WithTemplateGetter(tg.GetTemplate)
+
+ s, err := rs.GetTemplate(ctx, "foo")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+
+ store.SetPrefix(db.DATATYPE_TEMPLATE)
+ err = store.Put(ctx, []byte("foo"), []byte("bar"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ s, err = rs.GetTemplate(ctx, "foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s != "bar" {
+ t.Fatalf("expected 'bar', got %s", s)
+ }
+
+ // test support check
+ store.SetPrefix(db.DATATYPE_BIN)
+ err = store.Put(ctx, []byte("xyzzy"), []byte("deadbeef"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rs.WithCodeGetter(tg.GetCode)
+ b, err := rs.GetCode(ctx, "xyzzy")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+
+ tg, err = NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rs.WithTemplateGetter(tg.GetTemplate)
+
+ rs.WithCodeGetter(tg.GetCode)
+ b, err = rs.GetCode(ctx, "xyzzy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(b, []byte("deadbeef")) {
+ t.Fatalf("expected 'deadbeef', got %x", b)
+ }
+
+ tg, err = NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN, db.DATATYPE_MENU)
+ if err != nil {
+ t.Fatal(err)
+ }
+ store.SetPrefix(db.DATATYPE_MENU)
+ err = store.Put(ctx, []byte("inky"), []byte("pinky"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ rs.WithMenuGetter(tg.GetMenu)
+
+}
diff --git a/resource/mem.go b/resource/mem.go
@@ -34,7 +34,7 @@ func(mr MemResource) getTemplate(ctx context.Context, sym string) (string, error
return r, nil
}
-func(mr MemResource) getCode(sym string) ([]byte, error) {
+func(mr MemResource) getCode(ctx context.Context, sym string) ([]byte, error) {
r, ok := mr.bytecodes[sym]
if !ok {
return nil, fmt.Errorf("unknown bytecode: %s", sym)
diff --git a/resource/mem_test.go b/resource/mem_test.go
@@ -36,7 +36,8 @@ func TestMemResourceCode(t *testing.T) {
rs := NewMemResource()
rs.AddBytecode("foo", []byte("bar"))
- r, err := rs.GetCode("foo")
+ ctx := context.Background()
+ r, err := rs.GetCode(ctx, "foo")
if err != nil {
t.Fatal(err)
}
@@ -44,7 +45,7 @@ func TestMemResourceCode(t *testing.T) {
fmt.Errorf("expected 'bar', got %x", r)
}
- _, err = rs.GetCode("bar")
+ _, err = rs.GetCode(ctx, "bar")
if err == nil {
t.Fatalf("expected error")
}
diff --git a/resource/resource.go b/resource/resource.go
@@ -4,6 +4,7 @@ import (
"context"
)
+
// Result contains the results of an external code operation.
type Result struct {
Content string // content value for symbol after execution.
@@ -14,7 +15,7 @@ type Result struct {
// EntryFunc is a function signature for retrieving value for a key
type EntryFunc func(ctx context.Context, sym string, input []byte) (Result, error)
-type CodeFunc func(sym string) ([]byte, error)
+type CodeFunc func(ctx context.Context, sym string) ([]byte, error)
type MenuFunc func(ctx context.Context, sym string) (string, error)
type TemplateFunc func(ctx context.Context, sym string) (string, error)
type FuncForFunc func(sym string) (EntryFunc, error)
@@ -22,7 +23,7 @@ type FuncForFunc func(sym string) (EntryFunc, error)
// Resource implementation are responsible for retrieving values and templates for symbols, and can render templates from value dictionaries.
type Resource interface {
GetTemplate(ctx context.Context, sym string) (string, error) // Get the template for a given symbol.
- GetCode(sym string) ([]byte, error) // Get the bytecode for the given symbol.
+ GetCode(ctx context.Context, sym string) ([]byte, error) // Get the bytecode for the given symbol.
GetMenu(ctx context.Context, sym string) (string, error) // Receive menu test for menu symbol.
FuncFor(sym string) (EntryFunc, error) // Resolve symbol content point for.
}
@@ -73,8 +74,8 @@ func(m MenuResource) FuncFor(sym string) (EntryFunc, error) {
}
// GetCode implements Resource interface
-func(m MenuResource) GetCode(sym string) ([]byte, error) {
- return m.codeFunc(sym)
+func(m MenuResource) GetCode(ctx context.Context, sym string) ([]byte, error) {
+ return m.codeFunc(ctx, sym)
}
// GetTemplate implements Resource interface