commit 8cf6dbaec7f7d0f2dbeaf0f14cd66c35cbf1588e
parent 9f848e7012246d654edd6da8c087673719a1ede2
Author: lash <dev@holbrook.no>
Date: Sun, 23 Apr 2023 09:20:50 +0100
Add mem resource
Diffstat:
9 files changed, 191 insertions(+), 13 deletions(-)
diff --git a/render/page.go b/render/page.go
@@ -56,12 +56,16 @@ func(pg *Page) WithError(err error) *Page {
return pg
}
+// WithOpaqueError sets an error which will be used for display regardless of which error was set using WithError
func(pg *Page) WithOpaqueError(err error) *Page {
pg.errConst = err
return pg
}
-func(pg *Page) Error() string {
+// Error implements error interface.
+//
+// This error is used internally by the renderer, and is not intended for direct use.
+func(pg *Page) ErrorString() string {
if pg.err != nil {
if pg.errConst != nil {
return pg.errConst.Error()
diff --git a/render/page_test.go b/render/page_test.go
@@ -1,9 +1,11 @@
package render
import (
+ "fmt"
"testing"
"git.defalsify.org/vise.git/cache"
+ "git.defalsify.org/vise.git/resource"
)
@@ -113,3 +115,19 @@ func TestStateMapSink(t *testing.T) {
t.Error(err)
}
}
+
+func TestWithError(t *testing.T) {
+ ca := cache.NewCache()
+ rs := resource.NewMemResource()
+ pg := NewPage(ca, rs)
+ ca.Push()
+
+ mn := NewMenu().WithOutputSize(32)
+ err := mn.Put("0", "aiee")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = fmt.Errorf("my humps")
+ pg = pg.WithMenu(mn).WithError(err)
+
+}
diff --git a/resource/fs.go b/resource/fs.go
@@ -19,7 +19,7 @@ type FsResource struct {
fns map[string]EntryFunc
}
-func NewFsResource(path string) (FsResource) {
+func NewFsResource(path string) FsResource {
absPath, err := filepath.Abs(path)
if err != nil {
panic(err)
diff --git a/resource/mem.go b/resource/mem.go
@@ -0,0 +1,62 @@
+package resource
+
+import (
+ "context"
+ "fmt"
+)
+
+type MemResource struct {
+ MenuResource
+ templates map[string]string
+ bytecodes map[string][]byte
+ funcs map[string]EntryFunc
+}
+
+func NewMemResource() MemResource {
+ mr := MemResource{
+ templates: make(map[string]string),
+ bytecodes: make(map[string][]byte),
+ funcs: make(map[string]EntryFunc),
+ }
+ mr.WithCodeGetter(mr.getCode)
+ mr.WithTemplateGetter(mr.getTemplate)
+ mr.WithEntryFuncGetter(mr.getFunc)
+ return mr
+}
+
+func(mr MemResource) getTemplate(sym string, ctx context.Context) (string, error) {
+ r, ok := mr.templates[sym]
+ if !ok {
+ return "", fmt.Errorf("unknown template symbol: %s", sym)
+ }
+ return r, nil
+}
+
+func(mr MemResource) getCode(sym string) ([]byte, error) {
+ r, ok := mr.bytecodes[sym]
+ if !ok {
+ return nil, fmt.Errorf("unknown bytecode: %s", sym)
+ }
+ return r, nil
+}
+
+func(mr MemResource) getFunc(sym string) (EntryFunc, error) {
+ r, ok := mr.funcs[sym]
+ if !ok {
+ return nil, fmt.Errorf("unknown entry func: %s", sym)
+ }
+ return r, nil
+}
+
+func(mr *MemResource) AddTemplate(sym string, tpl string) {
+ mr.templates[sym] = tpl
+}
+
+
+func(mr *MemResource) AddEntryFunc(sym string, fn EntryFunc) {
+ mr.funcs[sym] = fn
+}
+
+func(mr *MemResource) AddBytecode(sym string, code []byte) {
+ mr.bytecodes[sym] = code
+}
diff --git a/resource/mem_test.go b/resource/mem_test.go
@@ -0,0 +1,75 @@
+package resource
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "testing"
+)
+
+func testEntry(sym string, input []byte, ctx context.Context) (Result, error) {
+ return Result{
+ Content: fmt.Sprintf("%sbar", input),
+ }, nil
+}
+
+func TestMemResourceTemplate(t *testing.T) {
+ rs := NewMemResource()
+ rs.AddTemplate("foo", "bar")
+
+ ctx := context.TODO()
+ r, err := rs.GetTemplate("foo", ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if r != "bar" {
+ fmt.Errorf("expected 'bar', got %s", r)
+ }
+
+ _, err = rs.GetTemplate("bar", ctx)
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+}
+
+func TestMemResourceCode(t *testing.T) {
+ rs := NewMemResource()
+ rs.AddBytecode("foo", []byte("bar"))
+
+ r, err := rs.GetCode("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(r, []byte("bar")) {
+ fmt.Errorf("expected 'bar', got %x", r)
+ }
+
+ _, err = rs.GetCode("bar")
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+}
+
+func TestMemResourceEntry(t *testing.T) {
+ rs := NewMemResource()
+ rs.AddEntryFunc("foo", testEntry)
+
+ fn, err := rs.FuncFor("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ctx := context.TODO()
+ r, err := fn("foo", []byte("xyzzy"), ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if r.Content != "foobar" {
+ fmt.Errorf("expected 'foobar', got %x", r.Content)
+ }
+
+ _, err = rs.FuncFor("bar")
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+}
diff --git a/resource/resource.go b/resource/resource.go
@@ -58,16 +58,16 @@ func(m *MenuResource) WithTemplateGetter(templateGetter TemplateFunc) *MenuResou
}
// FuncFor implements Resource interface
-func(m *MenuResource) FuncFor(sym string) (EntryFunc, error) {
+func(m MenuResource) FuncFor(sym string) (EntryFunc, error) {
return m.funcFunc(sym)
}
// GetCode implements Resource interface
-func(m *MenuResource) GetCode(sym string) ([]byte, error) {
+func(m MenuResource) GetCode(sym string) ([]byte, error) {
return m.codeFunc(sym)
}
// GetTemplate implements Resource interface
-func(m *MenuResource) GetTemplate(sym string, ctx context.Context) (string, error) {
+func(m MenuResource) GetTemplate(sym string, ctx context.Context) (string, error) {
return m.templateFunc(sym, ctx)
}
diff --git a/vm/input.go b/vm/input.go
@@ -20,16 +20,19 @@ var (
)
+// InvalidInputError indicates client input that was unhandled by the bytecode (INCMP fallthrough)
type InvalidInputError struct {
input string
}
+// NewInvalidInputError creates a new InvalidInputError
func NewInvalidInputError(input string) error {
return InvalidInputError{input}
}
+// Error implements error interface.
func(e InvalidInputError) Error() string {
- return "invalid input"
+ return fmt.Sprintf("invalid input: '%s'", e.input)
}
// CheckInput validates the given byte string as client input.
diff --git a/vm/runner.go b/vm/runner.go
@@ -10,21 +10,22 @@ import (
"git.defalsify.org/vise.git/state"
)
+// ExternalCodeError indicates an error that occurred when resolving an external code symbol (LOAD, RELOAD).
type ExternalCodeError struct {
sym string
err error
}
+// NewExternalCodeError creates a new ExternalCodeError.
func NewExternalCodeError(sym string, err error) error {
return ExternalCodeError{sym, err}
}
+// Error implements error interface
func(e ExternalCodeError) Error() string {
return fmt.Sprintf("[%s] %v", e.sym, e.err)
}
-
-
// Vm holds sub-components mutated by the vm execution.
// TODO: Renderer should be passed to avoid proxy methods not strictly related to vm operation
type Vm struct {
@@ -217,7 +218,11 @@ func(vm *Vm) RunDeadCheck(b []byte, ctx context.Context) ([]byte, error) {
if location == "" {
return b, fmt.Errorf("dead runner with no current location")
}
- cerr := fmt.Errorf("invalid input")
+ input, err := vm.st.GetInput()
+ if err != nil {
+ input = []byte("(no input)")
+ }
+ cerr := NewInvalidInputError(string(input))
vm.pg.WithError(cerr)
b = NewLine(nil, MOVE, []string{"_catch"}, nil, nil)
return b, nil
diff --git a/vm/runner_test.go b/vm/runner_test.go
@@ -363,13 +363,24 @@ func TestRunArgInvalid(t *testing.T) {
st.Down("root")
b := NewLine(nil, INCMP, []string{"bar", "baz"}, nil, nil)
- b, err = vm.Run(b, context.TODO())
+ ctx := context.TODO()
+ b, err = vm.Run(b, ctx)
if err != nil {
t.Fatal(err)
}
- r, _ := st.Where()
- if r != "_catch" {
- t.Fatalf("expected where-state _catch, got %v", r)
+ location, _ := st.Where()
+ if location != "_catch" {
+ t.Fatalf("expected where-state _catch, got %v", location)
+ }
+
+ r, err := vm.Render(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `invalid input: 'foo'
+0:repent`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s", expect, r)
}
}