Fixes made as part of updating the user manual

This commit is contained in:
Leon Mika 2023-03-07 22:28:10 +11:00
parent 72b7a40e23
commit cbbdea75a0
12 changed files with 138 additions and 52 deletions

View file

@ -0,0 +1,55 @@
/**
* Builtins adopted and modified from Taramin
* Copyright (c) 2022 Curtis Myzie
*/
package scriptmanager
import (
"context"
"github.com/cloudcmds/tamarin/object"
"log"
)
func printBuiltin(ctx context.Context, args ...object.Object) object.Object {
env := scriptEnvFromCtx(ctx)
prefix := "script " + env.filename + ":"
values := make([]interface{}, len(args)+1)
values[0] = prefix
for i, arg := range args {
switch arg := arg.(type) {
case *object.String:
values[i+1] = arg.Value()
default:
values[i+1] = arg.Inspect()
}
}
log.Println(values...)
return object.Nil
}
func printfBuiltin(ctx context.Context, args ...object.Object) object.Object {
env := scriptEnvFromCtx(ctx)
prefix := "script " + env.filename + ":"
numArgs := len(args)
if numArgs < 1 {
return object.Errorf("type error: printf() takes 1 or more arguments (%d given)", len(args))
}
format, err := object.AsString(args[0])
if err != nil {
return err
}
var values = []interface{}{prefix}
for _, arg := range args[1:] {
switch arg := arg.(type) {
case *object.String:
values = append(values, arg.Value())
default:
values = append(values, arg.Interface())
}
}
log.Printf("%s "+format, values...)
return object.Nil
}

View file

@ -31,6 +31,8 @@ func (m *extModule) register(scp *scope.Scope) {
}
func (m *extModule) command(ctx context.Context, args ...object.Object) object.Object {
thisEnv := scriptEnvFromCtx(ctx)
if err := arg.Require("ext.command", 2, args); err != nil {
return err
}
@ -56,7 +58,9 @@ func (m *extModule) command(ctx context.Context, args ...object.Object) object.O
objArgs[i] = object.NewString(a)
}
ctx = ctxWithOptions(ctx, m.scriptPlugin.scriptService.options)
newEnv := thisEnv
newEnv.options = m.scriptPlugin.scriptService.options
ctx = ctxWithScriptEnv(ctx, newEnv)
res := callFn(ctx, fnRes.Scope(), fnRes, objArgs)
if object.IsError(res) {
@ -74,6 +78,8 @@ func (m *extModule) command(ctx context.Context, args ...object.Object) object.O
}
func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) object.Object {
thisEnv := scriptEnvFromCtx(ctx)
if err := arg.Require("ext.key_binding", 3, args); err != nil {
return err
}
@ -112,7 +118,9 @@ func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) objec
objArgs[i] = object.NewString(a)
}
ctx = ctxWithOptions(ctx, m.scriptPlugin.scriptService.options)
newEnv := thisEnv
newEnv.options = m.scriptPlugin.scriptService.options
ctx = ctxWithScriptEnv(ctx, newEnv)
res := callFn(ctx, fnRes.Scope(), fnRes, objArgs)
if object.IsError(res) {

View file

@ -22,7 +22,7 @@ func (om *osModule) exec(ctx context.Context, args ...object.Object) object.Obje
return objErr
}
opts := optionFromCtx(ctx)
opts := scriptEnvFromCtx(ctx).options
if !opts.Permissions.AllowShellCommands {
return object.NewErrResult(object.Errorf("permission error: no permission to shell out"))
}
@ -46,7 +46,7 @@ func (om *osModule) env(ctx context.Context, args ...object.Object) object.Objec
return objErr
}
opts := optionFromCtx(ctx)
opts := scriptEnvFromCtx(ctx).options
if !opts.Permissions.AllowEnv {
return object.Nil
}

View file

@ -39,12 +39,12 @@ func TestModSession_Table(t *testing.T) {
table := session.current_table()
assert(table.name == "test_table")
assert(table.keys["partition"] == "pk")
assert(table.keys["sort"] == "sk")
assert(table.keys["hash"] == "pk")
assert(table.keys["range"] == "sk")
assert(len(table.gsis) == 1)
assert(table.gsis[0].name == "index-1")
assert(table.gsis[0].keys["partition"] == "ipk")
assert(table.gsis[0].keys["sort"] == "isk")
assert(table.gsis[0].keys["hash"] == "ipk")
assert(table.gsis[0].keys["range"] == "isk")
assert(table == session.result_set().table)
`)

View file

@ -40,7 +40,7 @@ func (um *uiModule) prompt(ctx context.Context, args ...object.Object) object.Ob
if hasResp {
return object.NewString(resp)
} else {
return object.NewError(ctx.Err())
return object.Nil
}
case <-ctx.Done():
return object.NewError(ctx.Err())

View file

@ -39,11 +39,12 @@ func TestModUI_Prompt(t *testing.T) {
mockedUIService.AssertExpectations(t)
})
t.Run("should return error if prompt was cancelled", func(t *testing.T) {
t.Run("should return nil if prompt was cancelled", func(t *testing.T) {
testFS := testScriptFile(t, "test.tm", `
ui.print("Hello, world")
var name = ui.prompt("What is your name? ")
ui.print("After")
ui.print(nil)
`)
promptChan := make(chan string)
@ -53,6 +54,8 @@ func TestModUI_Prompt(t *testing.T) {
mockedUIService := mocks.NewUIService(t)
mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, world")
mockedUIService.EXPECT().PrintMessage(mock.Anything, "After")
mockedUIService.EXPECT().PrintMessage(mock.Anything, "nil")
mockedUIService.EXPECT().Prompt(mock.Anything, "What is your name? ").Return(promptChan)
srv := scriptmanager.New(scriptmanager.WithFS(testFS))
@ -61,9 +64,8 @@ func TestModUI_Prompt(t *testing.T) {
})
err := <-srv.RunAdHocScript(ctx, "test.tm")
assert.Error(t, err)
assert.NoError(t, err)
mockedUIService.AssertNotCalled(t, "Prompt", "after")
mockedUIService.AssertExpectations(t)
})

View file

@ -34,15 +34,21 @@ type Permissions struct {
AllowEnv bool
}
type optionCtxKeyType struct{}
// scriptEnv is the runtime environment for a particular script execution
type scriptEnv struct {
filename string
options Options
}
var optionCtxKey = optionCtxKeyType{}
type scriptEnvKeyType struct{}
func optionFromCtx(ctx context.Context) Options {
perms, _ := ctx.Value(optionCtxKey).(Options)
var scriptEnvKey = scriptEnvKeyType{}
func scriptEnvFromCtx(ctx context.Context) scriptEnv {
perms, _ := ctx.Value(scriptEnvKey).(scriptEnv)
return perms
}
func ctxWithOptions(ctx context.Context, perms Options) context.Context {
return context.WithValue(ctx, optionCtxKey, perms)
func ctxWithScriptEnv(ctx context.Context, perms scriptEnv) context.Context {
return context.WithValue(ctx, scriptEnvKey, perms)
}

View file

@ -177,27 +177,6 @@ func (i *itemProxy) value(ctx context.Context, args ...object.Object) object.Obj
return object.NewError(err)
}
return tVal
// TODO
//switch v := av.(type) {
//case *types.AttributeValueMemberS:
// return object.NewString(v.Value)
//case *types.AttributeValueMemberN:
// // TODO: better
// f, err := strconv.ParseFloat(v.Value, 64)
// if err != nil {
// return object.NewError(errors.Errorf("value error: invalid N value: %v", v.Value))
// }
// return object.NewFloat(f)
//case *types.AttributeValueMemberBOOL:
// if v.Value {
// return object.True
// }
// return object.False
//case *types.AttributeValueMemberNULL:
// return object.Nil
//}
//return object.NewError(errors.New("TODO"))
}
func (i *itemProxy) setValue(ctx context.Context, args ...object.Object) object.Object {

View file

@ -3,10 +3,12 @@ package scriptmanager
import (
"context"
"github.com/cloudcmds/tamarin/exec"
"github.com/cloudcmds/tamarin/object"
"github.com/cloudcmds/tamarin/scope"
"github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
"github.com/pkg/errors"
"io/fs"
"log"
"os"
"path/filepath"
"strings"
@ -86,7 +88,7 @@ func (s *Service) StartAdHocScript(ctx context.Context, filename string, errChan
func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan chan error) {
defer close(errChan)
code, err := s.readScript(filename)
code, err := s.readScript(filename, true)
if err != nil {
errChan <- errors.Wrapf(err, "cannot load script file %v", filename)
return
@ -94,12 +96,16 @@ func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan
scp := scope.New(scope.Opts{Parent: s.parentScope()})
ctx = ctxWithOptions(ctx, s.options)
ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options})
if _, err = exec.Execute(ctx, exec.Opts{
Input: string(code),
File: filename,
Scope: scp,
Builtins: []*object.Builtin{
object.NewBuiltin("print", printBuiltin),
object.NewBuiltin("printf", printfBuiltin),
},
}); err != nil {
errChan <- errors.Wrapf(err, "script %v", filename)
return
@ -114,7 +120,7 @@ type loadedScriptResult struct {
func (s *Service) loadScript(ctx context.Context, filename string, resChan chan loadedScriptResult) {
defer close(resChan)
code, err := s.readScript(filename)
code, err := s.readScript(filename, false)
if err != nil {
resChan <- loadedScriptResult{err: errors.Wrapf(err, "cannot load script file %v", filename)}
return
@ -129,7 +135,7 @@ func (s *Service) loadScript(ctx context.Context, filename string, resChan chan
(&extModule{scriptPlugin: newPlugin}).register(scp)
ctx = ctxWithOptions(ctx, s.options)
ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename), options: s.options})
if _, err = exec.Execute(ctx, exec.Opts{
Input: string(code),
@ -143,8 +149,33 @@ func (s *Service) loadScript(ctx context.Context, filename string, resChan chan
resChan <- loadedScriptResult{scriptPlugin: newPlugin}
}
func (s *Service) readScript(filename string) ([]byte, error) {
func (s *Service) readScript(filename string, allowCwd bool) ([]byte, error) {
if allowCwd {
if cwd, err := os.Getwd(); err == nil {
fullScriptPath := filepath.Join(cwd, filename)
log.Printf("checking %v", fullScriptPath)
if stat, err := os.Stat(fullScriptPath); err == nil && !stat.IsDir() {
code, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
return code, nil
}
} else {
log.Printf("warn: cannot get cwd for reading script %v: %v", filename, err)
}
}
if strings.HasPrefix(filename, string(filepath.Separator)) {
code, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
return code, nil
}
for _, currFS := range s.lookupPaths {
log.Printf("checking %v/%v", currFS, filename)
stat, err := fs.Stat(currFS, filename)
if err != nil {
if errors.Is(err, os.ErrNotExist) {

View file

@ -8,8 +8,8 @@ import (
)
const (
tableProxyPartitionKey = "partition"
tableProxySortKey = "sort"
tableProxyPartitionKey = "hash"
tableProxySortKey = "range"
)
type tableProxy struct {
@ -42,6 +42,13 @@ func (t *tableProxy) GetAttr(name string) (object.Object, bool) {
case "name":
return object.NewString(t.table.Name), true
case "keys":
if t.table.Keys.SortKey == "" {
return object.NewMap(map[string]object.Object{
tableProxyPartitionKey: object.NewString(t.table.Keys.PartitionKey),
tableProxySortKey: object.Nil,
}), true
}
return object.NewMap(map[string]object.Object{
tableProxyPartitionKey: object.NewString(t.table.Keys.PartitionKey),
tableProxySortKey: object.NewString(t.table.Keys.SortKey),
@ -66,11 +73,11 @@ func newTableIndexProxy(gsi models.TableGSI) object.Object {
}
func (t tableIndexProxy) Type() object.Type {
return "index"
return "table_index"
}
func (t tableIndexProxy) Inspect() string {
return "index(gsi," + t.gsi.Name + ")"
return "table_index(gsi," + t.gsi.Name + ")"
}
func (t tableIndexProxy) Interface() interface{} {

View file

@ -139,9 +139,8 @@ func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
newTextInput, cmd := s.textInput.Update(msg)
s.textInput = newTextInput
return s, cmd
s.textInput = cc.Collect(s.textInput.Update(msg)).(textinput.Model)
return s, cc.Cmd()
} else {
s.statusMessage = ""
}

View file

@ -95,7 +95,6 @@ func main() {
for i := 0; i < totalItems; i++ {
if err := tableService.Put(ctx, inventoryTableInfo, models.Item{
"pk": &types.AttributeValueMemberS{Value: key},
"sk": &types.AttributeValueMemberN{Value: fmt.Sprint(i)},
"uuid": &types.AttributeValueMemberS{Value: gofakeit.UUID()},
}); err != nil {
log.Fatalln(err)