From 291d1439f4bdbb66bd8de655efa68177d0ccc0e2 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Mon, 26 May 2025 21:45:36 +1000 Subject: [PATCH] Removed the script controller --- cmd/dynamo-browse/main.go | 8 - go.mod | 49 +- go.sum | 239 +--------- .../common/ui/commandctrl/cmdpacks/stdcmds.go | 7 + .../ui/commandctrl/cmdpacks/stdcmds_test.go | 1 - internal/dynamo-browse/controllers/scripts.go | 290 ------------ .../dynamo-browse/controllers/scripts_test.go | 192 -------- .../dynamo-browse/controllers/tableread.go | 3 - .../controllers/tablewrite_test.go | 12 - .../services/scriptmanager/builtins.go | 102 ----- .../services/scriptmanager/iface.go | 38 -- .../scriptmanager/mocks/SessionService.go | 216 --------- .../services/scriptmanager/mocks/UIService.go | 116 ----- .../services/scriptmanager/modext.go | 270 ----------- .../services/scriptmanager/modext_test.go | 151 ------- .../services/scriptmanager/modos_test.go | 56 --- .../services/scriptmanager/modsession.go | 145 ------ .../services/scriptmanager/modsession_test.go | 426 ------------------ .../services/scriptmanager/modui.go | 58 --- .../services/scriptmanager/modui_test.go | 100 ---- .../services/scriptmanager/opts.go | 26 -- .../services/scriptmanager/relitem.go | 57 --- .../services/scriptmanager/resultsetproxy.go | 337 -------------- .../scriptmanager/resultsetproxy_test.go | 355 --------------- .../services/scriptmanager/scrsched.go | 53 --- .../services/scriptmanager/service.go | 250 ---------- .../services/scriptmanager/service_test.go | 150 ------ .../services/scriptmanager/serviceopts.go | 11 - .../services/scriptmanager/tableproxy.go | 138 ------ .../services/scriptmanager/typemapping.go | 135 ------ .../services/scriptmanager/types.go | 35 -- internal/dynamo-browse/ui/model.go | 160 ------- 32 files changed, 24 insertions(+), 4162 deletions(-) delete mode 100644 internal/dynamo-browse/controllers/scripts.go delete mode 100644 internal/dynamo-browse/controllers/scripts_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/builtins.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/iface.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/mocks/SessionService.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/mocks/UIService.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modext.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modext_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modos_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modsession.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modsession_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modui.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/modui_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/opts.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/relitem.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/resultsetproxy.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/resultsetproxy_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/scrsched.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/service.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/service_test.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/serviceopts.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/tableproxy.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/typemapping.go delete mode 100644 internal/dynamo-browse/services/scriptmanager/types.go diff --git a/cmd/dynamo-browse/main.go b/cmd/dynamo-browse/main.go index d1c4d08..3c5c4bf 100644 --- a/cmd/dynamo-browse/main.go +++ b/cmd/dynamo-browse/main.go @@ -28,7 +28,6 @@ import ( "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs" keybindings_service "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/keybindings" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/viewsnapshot" "github.com/lmika/dynamo-browse/internal/dynamo-browse/ui" @@ -107,7 +106,6 @@ func main() { tableService := tables.NewService(dynamoProvider, settingStore) workspaceService := viewsnapshot.NewService(resultSetSnapshotStore) itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo) - scriptManagerService := scriptmanager.New() jobsService := jobs.NewService(eventBus) inputHistoryService := inputhistory.New(inputHistoryStore) @@ -122,7 +120,6 @@ func main() { inputHistoryService, eventBus, pasteboardProvider, - scriptManagerService, *flagTable, ) tableWriteController := controllers.NewTableWriteController(state, tableService, jobsController, tableReadController, settingStore) @@ -130,7 +127,6 @@ func main() { exportController := controllers.NewExportController(state, tableService, jobsController, columnsController, pasteboardProvider) settingsController := controllers.NewSettingsController(settingStore, eventBus) keyBindings := keybindings.Default() - //scriptController := controllers.NewScriptController(scriptManagerService, tableReadController, jobsController, settingsController, eventBus) if *flagQuery != "" { if *flagTable == "" { @@ -177,7 +173,6 @@ func main() { if err != nil { cli.Fatalf("cannot setup command controller: %v", err) } - //commandController.AddCommandLookupExtension(scriptController) commandController.SetCommandCompletionProvider(columnsController) model := ui.NewModel( @@ -189,7 +184,6 @@ func main() { jobsController, itemRendererService, commandController, - //scriptController, eventBus, keyBindingController, pasteboardProvider, @@ -203,8 +197,6 @@ func main() { p := tea.NewProgram(model, tea.WithAltScreen()) jobsController.SetMessageSender(p.Send) - //scriptController.Init() - //scriptController.SetMessageSender(p.Send) if err := commandController.LoadExtensions(context.Background(), strings.Split(*flagExtDir, string(os.PathListSeparator))); err != nil { fmt.Printf("Unable to load extensions: %v", err) diff --git a/go.mod b/go.mod index 3113de7..18738aa 100644 --- a/go.mod +++ b/go.mod @@ -13,14 +13,11 @@ require ( github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12 github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11 - github.com/aws/aws-sdk-go-v2/service/sqs v1.23.2 - github.com/aws/aws-sdk-go-v2/service/ssm v1.24.0 github.com/brianvoe/gofakeit/v6 v6.15.0 github.com/calyptia/go-bubble-table v0.2.1 github.com/charmbracelet/bubbles v0.14.0 github.com/charmbracelet/bubbletea v0.22.1 github.com/charmbracelet/lipgloss v0.6.0 - github.com/cloudcmds/tamarin v1.0.0 github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538 github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f @@ -32,65 +29,33 @@ require ( github.com/stretchr/testify v1.9.0 golang.design/x/clipboard v0.6.2 golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a + ucl.lmika.dev v0.0.0-20250525023717-3076897eb73e ) require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879 // indirect - github.com/anthonynsimon/bild v0.13.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26 // indirect - github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.0 // indirect - github.com/aws/aws-sdk-go-v2/service/cloudfront v1.26.8 // indirect - github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.27.1 // indirect - github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.26.2 // indirect github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ebs v1.16.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ec2 v1.102.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.18.13 // indirect - github.com/aws/aws-sdk-go-v2/service/ecs v1.27.4 // indirect - github.com/aws/aws-sdk-go-v2/service/eks v1.27.14 // indirect - github.com/aws/aws-sdk-go-v2/service/elasticache v1.27.2 // indirect - github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.19.2 // indirect - github.com/aws/aws-sdk-go-v2/service/glue v1.52.0 // indirect - github.com/aws/aws-sdk-go-v2/service/iam v1.21.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29 // indirect github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3 // indirect - github.com/aws/aws-sdk-go-v2/service/kinesis v1.17.14 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.22.2 // indirect - github.com/aws/aws-sdk-go-v2/service/lambda v1.37.0 // indirect - github.com/aws/aws-sdk-go-v2/service/rds v1.46.0 // indirect - github.com/aws/aws-sdk-go-v2/service/redshift v1.28.0 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.36.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sfn v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sns v1.20.13 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect - github.com/aws/aws-sdk-go-v2/service/wafv2 v1.35.1 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/aymanbagabas/go-osc52 v1.0.3 // indirect github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.4.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lunixbochs/vtclean v1.0.0 // indirect github.com/mattn/go-isatty v0.0.17 // indirect @@ -98,24 +63,18 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.13.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/risor-io/risor v1.4.0 // indirect github.com/rivo/uniseg v0.4.2 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect - github.com/tidwall/gjson v1.14.3 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/wI2L/jsondiff v0.3.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect - golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp/shiny v0.0.0-20230213192124-5e25df0256eb // indirect golang.org/x/image v0.5.0 // indirect golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect + golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - ucl.lmika.dev v0.0.0-20250525023717-3076897eb73e // indirect ) diff --git a/go.sum b/go.sum index 2b9e341..462dd4c 100644 --- a/go.sum +++ b/go.sum @@ -1,150 +1,57 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879 h1:M5ptEKnqKqpFTKbe+p5zEf3ro1deJ6opUz5j3g3/ErQ= github.com/Sereal/Sereal v0.0.0-20220220040404-e0d1e550e879/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= -github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg= -github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs= -github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= -github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo= -github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM= +github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= +github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= -github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= -github.com/anthonynsimon/bild v0.13.0 h1:mN3tMaNds1wBWi1BrJq0ipDBhpkooYfu7ZFSMhXt1C8= -github.com/anthonynsimon/bild v0.13.0/go.mod h1:tpzzp0aYkAsMi1zmfhimaDyX1xjn2OUc1AJZK/TF0AE= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/asdine/storm v2.1.2+incompatible h1:dczuIkyqwY2LrtXPz8ixMrU/OFgZp71kbKTHGrXYt/Q= github.com/asdine/storm v2.1.2+incompatible/go.mod h1:RarYDc9hq1UPLImuiXK3BIWPJLdIygvV3PsInK0FbVQ= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4fhkf8LXvC7w= -github.com/aws/aws-sdk-go-v2 v1.16.1/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= -github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82l4ru9Jvnuo= -github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs= github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA= github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw= -github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o= -github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to= github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk= github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12 h1:ama2cD4WaH6+8Gq/M/g+ZumPmmqCyanr+6Sm+iJVxfA= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.12/go.mod h1:tPnUO5mS3JThpwfq4Q8iPd745s7yh6fGPqDUEBw+Wv4= github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39 h1:PhgfvgqwMFQKwOcxLV7V3lNDVnR3ZUWzoB6T9oCFpR4= github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.4.39/go.mod h1:/GkvC7uHpK50ilKkKx9I2gZiI/ieZbKjS2aah1rT9uE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.8/go.mod h1:LnTQMTqbKsbtt+UI5+wPsB7jedW+2ZgozoPG8k6cMxg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.2/go.mod h1:1x4ZP3Z8odssdhuLI+/1Tqw6Pt/VAaP4Tr8EUxHvPXE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26 h1:wscW+pnn3J1OYnanMnza5ZVYXLX4cKk5rAvUAl4Qu+c= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26/go.mod h1:MtYiox5gvyB+OyP0Mr0Sm/yzbEAIPL9eijj/ouHAPw0= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.0 h1:XbDkc4FLeg1RfnqeblfbJvaEabqq9ByZl4zqyPFkfSc= -github.com/aws/aws-sdk-go-v2/service/cloudformation v1.30.0/go.mod h1:SwQFcCs9Rog8hSHm+81KBkAK+UKLXErA/1ChaEI8mLE= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.26.8 h1:loRDtQ0vT0+JCB0hQBCfv95tttEzJ1rqSaTDy5cpy0A= -github.com/aws/aws-sdk-go-v2/service/cloudfront v1.26.8/go.mod h1:YTd4wGn2beCF9wkSTpEcupk79zDFYJk2Ca76B8YyvJg= -github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.27.1 h1:Qw1G/M7eanpm6s/URkG1UuRLKEnRnpUvkUb7NMVvWb8= -github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.27.1/go.mod h1:oKRYqorIUkfAVmX03+lpv3tW5WelDpaliqzTwmCj/k8= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.26.2 h1:PWGu2JhCb/XJlJ7SSFJq76pxk4xWsN76nZxh7TzMHx0= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.26.2/go.mod h1:2KOZkkzMDZCo/aLzPhys06mHNkiU74u85aMJA3PLRvg= -github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.3 h1:MxOpCZ+o9+AIeQHi2ocW7H4D7p0LhEkmetETVvDnkvg= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.3/go.mod h1:nkpC9xkh+3vdxmhqN8Ac10pgV14DsJDLzUsV2CcS+44= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11 h1:tLTGNAsazbfjfjW1k/i43kyCcyTTTTFaD93H7JbSbbs= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11/go.mod h1:W1oiFegjVosgjIwb2Vv45jiCQT1ee8x85u8EyZRYLes= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3 h1:B+bkmCnNJi194pu9aTtYUe8f4EPXafC+xfU+zciVxdg= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.3/go.mod h1:bRphLmXQD9Ux4jLcFEwyrWdmuPTj2Lh8VGl9wILuJII= -github.com/aws/aws-sdk-go-v2/service/ebs v1.16.14 h1:DosI4CvEUo6/V21pDspzYkOa2X3Zwy5XS/cbPFiqDv0= -github.com/aws/aws-sdk-go-v2/service/ebs v1.16.14/go.mod h1:yVTqVHjnrbAj6FvhTQfjNgwQbjPbDUUvA1x4IpXFmrE= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.102.0 h1:P4dyjm49F2kKws0FpouBC6fjVImACXKt752+CWa01lM= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.102.0/go.mod h1:tIctCeX9IbzsUTKHt53SVEcgyfxV2ElxJeEB+QUbc4M= -github.com/aws/aws-sdk-go-v2/service/ecr v1.18.13 h1:hF7MUVNjubetjggZDtn3AmqCJzD7EUi//tSdxMYPm7U= -github.com/aws/aws-sdk-go-v2/service/ecr v1.18.13/go.mod h1:XwEFO35g0uN/SftK0asWxh8Rk6DOx37R83TmWe2tzEE= -github.com/aws/aws-sdk-go-v2/service/ecs v1.27.4 h1:F1N0Eh5EGRRY9QpF+tMTkx8Wb59DkQWE91Xza/9dk1c= -github.com/aws/aws-sdk-go-v2/service/ecs v1.27.4/go.mod h1:0irnFofeEZwT7uTjSkNVcSQJbWRqZ9BRoxhKjt1BObM= -github.com/aws/aws-sdk-go-v2/service/eks v1.27.14 h1:47HQVuJXgwvuoc4AT3rVdm77H0qGFbFnsuE4PRT+xX0= -github.com/aws/aws-sdk-go-v2/service/eks v1.27.14/go.mod h1:QxuWcm9rlLkW3aEV8tiDzqZewnNSNUZfnqJvo1Nv9A0= -github.com/aws/aws-sdk-go-v2/service/elasticache v1.27.2 h1:IC9XLGcT3yEkziTlX7PX54km7cHJnltlV7Ppwq2+7ik= -github.com/aws/aws-sdk-go-v2/service/elasticache v1.27.2/go.mod h1:+oJhn/SIud310/2LLSVmlNZmExmlYPaGCLmUsnq5JZc= -github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.19.2 h1:Zam6yofBgdtP13laNoeA+DA9wlKJNooU8p3CWw6xLaI= -github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.19.2/go.mod h1:dehjpZ00q0RJcBUOUEysaj7zHK2rHSS4ePp89MsFiaI= -github.com/aws/aws-sdk-go-v2/service/glue v1.52.0 h1:ukSf8ZdoZ6AygsUWIjj177wLOXljxBspBaNMgvx6fRA= -github.com/aws/aws-sdk-go-v2/service/glue v1.52.0/go.mod h1:wMCE0B6l8eHb57l2DMYCGxt0rHIfcu3RvIY7SAfc+Fs= -github.com/aws/aws-sdk-go-v2/service/iam v1.21.0 h1:8hEpu60CWlrp7iEBUFRZhgPoX6+gadaGL1sD4LoRYS0= -github.com/aws/aws-sdk-go-v2/service/iam v1.21.0/go.mod h1:aQZ8BI+reeaY7RI/QQp7TKCSUHOesTdrzzylp3CW85c= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29 h1:zZSLP3v3riMOP14H7b4XP0uyfREDQOYv2cqIrvTXDNQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29/go.mod h1:z7EjRjVwZ6pWcWdI2H64dKttvzaP99jRIj5hphW0M5U= -github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.22 h1:6zEryIiJOSk5/OcVHzkPDwzNBQ2atYCTShyA7TqkuxA= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.22/go.mod h1:moeOz5SKfY0p6pNIChdPIQdfaUfWI67+OVe0/r6+aGY= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28 h1:/D994rtMQd1jQ2OY+7tvUlMlrv1L1c7Xtma/FhkbVtY= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28/go.mod h1:3bJI2pLY3ilrqO5EclusI1GbjFJh1iXYrhOItf2sjKw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3 h1:dBL3StFxHtpBzJJ/mNEsjXVgfO+7jR0dAIEwLqMapEA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3/go.mod h1:f1QyiAsvIv4B49DmCqrhlXqyaR+0IxMmyX+1P+AnzOM= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.17.14 h1:oSw0SQN9cKeYvCUYfPul7bH11b8E9I9BnoVUme3iSaU= -github.com/aws/aws-sdk-go-v2/service/kinesis v1.17.14/go.mod h1:omXkSCk1T1difhE8wVaecXNeerY6jmpFFu49ngjEDQk= -github.com/aws/aws-sdk-go-v2/service/kms v1.22.2 h1:jwmtdM1/l1DRNy5jQrrYpsQm8zwetkgeqhAqefDr1yI= -github.com/aws/aws-sdk-go-v2/service/kms v1.22.2/go.mod h1:aNfh11Smy55o65PB3MyKbkM8BFyFUcZmj1k+4g8eNfg= -github.com/aws/aws-sdk-go-v2/service/lambda v1.37.0 h1:xzyM5ZR9kZW0/Bkw5EiihOy6B+BYclp5K+yb6OHjc7s= -github.com/aws/aws-sdk-go-v2/service/lambda v1.37.0/go.mod h1:Q8zQi5nZpjUF/H55dKEpKfEvFWJkgZzjjqvDb2AR5b4= -github.com/aws/aws-sdk-go-v2/service/rds v1.46.0 h1:uv2LAciZRd5lEXzJo2u92tdZh/JxcVL7YLC51D4NLG4= -github.com/aws/aws-sdk-go-v2/service/rds v1.46.0/go.mod h1:goBDR4OPrsnKpYyU0GHGcEnlTmL8O+eKGsWeyOAFJ5M= -github.com/aws/aws-sdk-go-v2/service/redshift v1.28.0 h1:tmhg03t7nNVSFqhxb8YpHqq8H1wwwrfEQv/rL7NkTAE= -github.com/aws/aws-sdk-go-v2/service/redshift v1.28.0/go.mod h1:x9am33DT5lVKUb0DH1UVbX+iFfpIqAKx6DAqB5Qu6jU= -github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3 h1:nJbE4+tHd8xpM1RB1ZF0/xTJnFd/ATz42ZC35lwXx0w= -github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3/go.mod h1:Cd4MnFoV+6fELBrgWXJ4Y09FrSkn/VjNPkOr1Yr1muU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.36.0 h1:lEmQ1XSD9qLk+NZXbgvLJI/IiTz7OIR2TYUTFH25EI4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.36.0/go.mod h1:aVbf0sko/TsLWHx30c/uVu7c62+0EAJ3vbxaJga0xCw= -github.com/aws/aws-sdk-go-v2/service/sfn v1.18.0 h1:1AIwJvCywFO4nGtHj7ZtKb9mhLpB5hToyjtE5OO6o/I= -github.com/aws/aws-sdk-go-v2/service/sfn v1.18.0/go.mod h1:41VgIwo6R/QE8DnFZ4RrP+f2w9xTzB77h3NRu/BzXyE= -github.com/aws/aws-sdk-go-v2/service/sns v1.20.13 h1:+ADGcDhddHTKyu6Qp3oZKootryteS7D3ODo2ZPDBgjQ= -github.com/aws/aws-sdk-go-v2/service/sns v1.20.13/go.mod h1:rWrvp9i8y/lX94lS7Kn/0iu9RY6vXzeKRqS/knVX8/c= -github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0 h1:dzWS4r8E9bA0TesHM40FSAtedwpTVCuTsLI8EziSqyk= -github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0/go.mod h1:IBTQMG8mtyj37OWg7vIXcg714Ntcb/LlYou/rZpvV1k= -github.com/aws/aws-sdk-go-v2/service/sqs v1.23.2 h1:Y2vfLiY3HmaMisuwx6fS2kMRYbajRXXB+9vesGVPseY= -github.com/aws/aws-sdk-go-v2/service/sqs v1.23.2/go.mod h1:TaV67b6JMD1988x/uMDop/JnMFK6v5d4Ru+sDmFg+ww= -github.com/aws/aws-sdk-go-v2/service/ssm v1.24.0 h1:p22U2yL/AeRToERGcZv1R26Yci5VQnWIrpzcZdG54cg= -github.com/aws/aws-sdk-go-v2/service/ssm v1.24.0/go.mod h1:chcyLYBEVRac/7rWJsD6cUHUR2osROwavvNqCplfwog= -github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 h1:1qLJeQGBmNQW3mBNzK2CFmrQNmoXWrscPqsrAaU1aTA= -github.com/aws/aws-sdk-go-v2/service/sso v1.9.0/go.mod h1:vCV4glupK3tR7pw7ks7Y4jYRL86VvxS+g5qk04YeWrU= github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY= github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w= -github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 h1:ksiDXhvNYg0D2/UFkLejsaz3LqpW5yjNQ8Nx9Sn2c0E= -github.com/aws/aws-sdk-go-v2/service/sts v1.14.0/go.mod h1:u0xMJKDvvfocRjiozsoZglVNXRG19043xzp3r2ivLIk= github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE= github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.35.1 h1:FtzLuTf9HPECIcKdBMtA16ZwZWOIj/r57Z3QuWuYfqc= -github.com/aws/aws-sdk-go-v2/service/wafv2 v1.35.1/go.mod h1:RBpb9oTsEgAUfyaTAT2hFC83DxtLxj+SQpcbhaXiHnU= -github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= @@ -162,51 +69,23 @@ github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJ github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= -github.com/cloudcmds/tamarin v1.0.0 h1:PhrJ74FCUJo24/nIPXnQe9E3WVEIYo4aG58pICOMDBE= -github.com/cloudcmds/tamarin v1.0.0/go.mod h1:U1aHBoAFtJbI9jzgaj8TUo9C6vfzUKzn1OhWKIdigVM= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.0.4 h1:r5O6y84qHX/z/HZV40JBdx2obsHz7/uRj5b+CcYEdeY= -github.com/jackc/pgx/v5 v5.0.4/go.mod h1:U0ynklHtgg43fue9Ly30w3OCSTDPlXjig9ghrNGaguQ= -github.com/jackc/pgx/v5 v5.4.1 h1:oKfB/FhuVtit1bBM3zNRRsZ925ZkMN3HXL+LgLUM9lE= -github.com/jackc/pgx/v5 v5.4.1/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -214,19 +93,18 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc h1:ZQrgZFsLzkw7o3CoDzsfBhx0bf/1rVBXrLy8dXKRe8o= github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc/go.mod h1:PyXUpnI3olx3bsPcHt98FGPX/KCFZ1Fi+hw1XLI6384= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e h1:0QkUe2ejnT/i+xbgGylMU1b+XnZponQKiPVNi+C/xgA= github.com/lmika/events v0.0.0-20200906102219-a2269cd4394e/go.mod h1:qtkBmNC9OfD0STtOR9sF55pQchjIfNlC3gzm4n8CrqM= github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538 h1:dtMPRNoDqDnnP3HgOvYhswcJVSqdISkYlCtGOjTqg6Q= github.com/lmika/go-bubble-table v0.2.2-0.20220616114432-6bbb2995e538/go.mod h1:0RT1upgKZ6qZ6B1SqseE3wWsPjSQRv/G/HjpYK8jNsg= -github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890 h1:mwl/exYV/WkBMeShqK7q+B2w2r+b0vP1TSA7clBn9kI= -github.com/lmika/gopkgs v0.0.0-20211210041137-0dc91e939890/go.mod h1:FH6OJSvYcJ9xY8CGs9yGgR89kMCK1UimuUQ6kE5YuJQ= github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f h1:tz68Lhc1oR15HVz69IGbtdukdH0x70kBDEvvj5pTXyE= github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f/go.mod h1:zHQvhjGXRro/Xp2C9dbC+ZUpE0gL4GYW75x1lk7hwzI= github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe h1:1UXS/6OFkbi6JrihPykmYO1VtsABB02QQ+YmYYzTY18= @@ -235,11 +113,9 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.10/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -250,8 +126,6 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= @@ -265,88 +139,42 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/risor-io/risor v0.8.0 h1:G0fpHMGztvocKVu8egkKNbvLy4Rsjkuk+0zReu2JSn8= -github.com/risor-io/risor v0.8.0/go.mod h1:lvatEIYxs6HL+X/Bm0R+Mq4Z9a5Y036mniw6DwUnqs0= -github.com/risor-io/risor v1.1.1 h1:J8rIZX/0HXhg/t2+QygksvP65XCWhg5QxRZrwZabhxE= -github.com/risor-io/risor v1.1.1/go.mod h1:0UMw7ZMbUKSPFgQyuHCFe7UuBUewBKX4K3By4ba1CBA= -github.com/risor-io/risor v1.4.0 h1:G17pWgq+N06jWvnaJVwos89tC5C4VMjqwGYRrTWleRM= -github.com/risor-io/risor v1.4.0/go.mod h1:+s/FeK0CdsTCCNZsHSp8EJa3u3mMrhqtNGLCv/GcW8Y= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= -github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/wI2L/jsondiff v0.3.0 h1:iTzQ9u/d86GE9RsBzVHX88f2EA1vQUboHwLhSQFc1s4= -github.com/wI2L/jsondiff v0.3.0/go.mod h1:y1IMzNNjlSsk3IUoJdRJO7VRBtzMvRgyo4Vu0LdHpTc= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= golang.design/x/clipboard v0.6.2 h1:a3Np4qfKnLWwfFJQhUWU3IDeRfmVuqWl+QPtP4CSYGw= golang.design/x/clipboard v0.6.2/go.mod h1:kqBSweBP0/im4SZGGjLrppH0D400Hnfo5WbFKSNK8N4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a h1:tlXy25amD5A7gOfbXdqCGN5k8ESEed/Ee1E5RcrYnqU= golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp/shiny v0.0.0-20230213192124-5e25df0256eb h1:gdeQX7xJSkTNF+Sw7++XNIOo4pGL0CjQv3N2Vm1Erxk= golang.org/x/exp/shiny v0.0.0-20230213192124-5e25df0256eb/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= @@ -362,13 +190,12 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -385,14 +212,10 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w= -golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -400,8 +223,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -422,43 +243,9 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -ucl.lmika.dev v0.0.0-20240427010304-6315afc54287 h1:llPHrjca54duvQx9PgMTFDhOW2VQiVvqV1CEHpO4AnY= -ucl.lmika.dev v0.0.0-20240427010304-6315afc54287/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4= -ucl.lmika.dev v0.0.0-20240501110514-25594c80d273 h1:+JpKw02VTAcOjJw7Q6juun/9hk9ypNSdTRlf+E4M5Nw= -ucl.lmika.dev v0.0.0-20240501110514-25594c80d273/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4= -ucl.lmika.dev v0.0.0-20240504001444-cf3a12bf0d4d h1:OqGmR0Y+OG6aFIOlXy2QwEHtuUNasYCh/6cxHokYQj4= -ucl.lmika.dev v0.0.0-20240504001444-cf3a12bf0d4d/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4= -ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281 h1:/M7phiv/0XVp3wKkOxEnGQysf8+RS6NOaBQZyUEoSsA= -ucl.lmika.dev v0.0.0-20240504013531-0dc9fd3c3281/go.mod h1:T6V4jIUxlWvMTgn4J752VDHNA8iyVrEX6v98EvDj8G4= -ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8 h1:vWttdW8GJWcTUQeJFbQHqCHJDLFWQ9nccUTx/lW2v8s= -ucl.lmika.dev v0.0.0-20250306030053-ad6d002a22e8/go.mod h1:FMP2ncSu4UxfvB0iA2zlebwL+1UPCARdyYNOrmi86A4= -ucl.lmika.dev v0.0.0-20250515115457-27b6cc0b92e2 h1:cvguOoQ0HVgLKbHH17ZHvAUFht6HXApLi0o8JOdaaNU= -ucl.lmika.dev v0.0.0-20250515115457-27b6cc0b92e2/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250517003439-109be33d1495 h1:r46r+7T59Drm+in7TEWKCZfFYIM0ZyZ26QjHAbj8Lto= -ucl.lmika.dev v0.0.0-20250517003439-109be33d1495/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250517115116-0f1ceba0902e h1:CQ+qPqI5lYiiEM0tNAr4jS0iMz16bFqOui5mU3AHsCU= -ucl.lmika.dev v0.0.0-20250517115116-0f1ceba0902e/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250517212052-51e35aa9a675 h1:kGKh3zj6lMzOrGAquFW7ROgx9/6nwJ8DXiSLtceRiak= -ucl.lmika.dev v0.0.0-20250517212052-51e35aa9a675/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250517212757-33d04ba18db4 h1:rnietWu2B+NXLqKfo7jgf6r+srMwxFa5eizywkq4LFk= -ucl.lmika.dev v0.0.0-20250517212757-33d04ba18db4/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250517213937-94aad417121d h1:CMcA8aQV6iiPK75EbHvoIVZhZmSggfrWNhK9BFm2aIg= -ucl.lmika.dev v0.0.0-20250517213937-94aad417121d/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250518024533-f4be44fcbc94 h1:x3IRtT1jbedblimi2hesKGBihg243+wNOSvagCPR0KU= -ucl.lmika.dev v0.0.0-20250518024533-f4be44fcbc94/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250518033831-f79e91e26d78 h1:lbOZUb6whYMLI4win5QL+eLSgqc3N9TtTgT8hTipNl8= -ucl.lmika.dev v0.0.0-20250518033831-f79e91e26d78/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250519111943-1173d163f5e3 h1:ZMQ1rkcAWa///c3bVvlXbtuqjfAWxDm01abQl3g/YVw= -ucl.lmika.dev v0.0.0-20250519111943-1173d163f5e3/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250519114239-7ca821016e9a h1:dzBBFCY50+MQcJaQ90swdDyjzag5oIhwdfqbmZkvX3Q= -ucl.lmika.dev v0.0.0-20250519114239-7ca821016e9a/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= -ucl.lmika.dev v0.0.0-20250519120409-53b05b5ba6f8 h1:h32JQi0d1MI86RaAMaEU7kvti4uSLX5XYe/nk2abApg= -ucl.lmika.dev v0.0.0-20250519120409-53b05b5ba6f8/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= ucl.lmika.dev v0.0.0-20250525023717-3076897eb73e h1:N+HzQUunDUvdjAzbSDtHQZVZ1k+XHbVgbNwmc+EKmlQ= ucl.lmika.dev v0.0.0-20250525023717-3076897eb73e/go.mod h1:/MMZKm6mOMtnY4I8TYEot4Pc8dKEy+/IAQo1VdpA5EY= diff --git a/internal/common/ui/commandctrl/cmdpacks/stdcmds.go b/internal/common/ui/commandctrl/cmdpacks/stdcmds.go index 088c0f9..748aaea 100644 --- a/internal/common/ui/commandctrl/cmdpacks/stdcmds.go +++ b/internal/common/ui/commandctrl/cmdpacks/stdcmds.go @@ -417,6 +417,13 @@ func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) { ucl.SetBuiltin("noisy-touch", sc.cmdNoisyTouch) ucl.SetBuiltin("rebind", sc.cmdRebind) + // Aliases + ucl.SetBuiltin("sa", sc.cmdSetAttr) + ucl.SetBuiltin("da", sc.cmdDelAttr) + ucl.SetBuiltin("np", sc.cmdNextPage) + ucl.SetBuiltin("w", sc.cmdPut) + ucl.SetBuiltin("q", sc.cmdQuit) + ucl.SetPseudoVar("resultset", resultSetPVar{sc.State, sc.ReadController}) ucl.SetPseudoVar("table", tablePVar{sc.State}) ucl.SetPseudoVar("item", itemPVar{sc.State}) diff --git a/internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go b/internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go index 55bef6e..63f099f 100644 --- a/internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go +++ b/internal/common/ui/commandctrl/cmdpacks/stdcmds_test.go @@ -126,7 +126,6 @@ func newService(t *testing.T, opts ...serviceOpt) *services { inputHistoryService, eventBus, pasteboardprovider.NilProvider{}, - nil, s.table, ) writeController := controllers.NewTableWriteController(state, service, jobsController, readController, settingStore) diff --git a/internal/dynamo-browse/controllers/scripts.go b/internal/dynamo-browse/controllers/scripts.go deleted file mode 100644 index ff2b8d6..0000000 --- a/internal/dynamo-browse/controllers/scripts.go +++ /dev/null @@ -1,290 +0,0 @@ -package controllers - -import ( - "context" - "fmt" - "log" - "strings" - "ucl.lmika.dev/ucl" - - tea "github.com/charmbracelet/bubbletea" - "github.com/lmika/dynamo-browse/internal/common/ui/commandctrl" - "github.com/lmika/dynamo-browse/internal/common/ui/events" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/relitems" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - bus "github.com/lmika/events" - "github.com/pkg/errors" -) - -type ScriptController struct { - scriptManager *scriptmanager.Service - tableReadController *TableReadController - jobController *JobsController - settingsController *SettingsController - eventBus *bus.Bus - sendMsg func(msg tea.Msg) -} - -func NewScriptController( - scriptManager *scriptmanager.Service, - tableReadController *TableReadController, - jobController *JobsController, - settingsController *SettingsController, - eventBus *bus.Bus, -) *ScriptController { - sc := &ScriptController{ - scriptManager: scriptManager, - tableReadController: tableReadController, - jobController: jobController, - settingsController: settingsController, - eventBus: eventBus, - } - - sessionImpl := &sessionImpl{sc: sc, lastSelectedItemIndex: -1} - scriptManager.SetIFaces(scriptmanager.Ifaces{ - UI: &uiImpl{sc: sc}, - Session: sessionImpl, - }) - - sessionImpl.subscribeToEvents(eventBus) - - // Setup event handling when settings have changed - eventBus.On(BusEventSettingsUpdated, func(name, value string) { - if !strings.HasPrefix(name, "script.") { - return - } - sc.Init() - }) - - return sc -} - -func (sc *ScriptController) Init() { - if lookupPaths, err := sc.settingsController.settings.ScriptLookupFS(); err == nil { - sc.scriptManager.SetLookupPaths(lookupPaths) - } else { - log.Printf("warn: script lookup paths are invalid: %v", err) - } -} - -func (sc *ScriptController) SetMessageSender(sendMsg func(msg tea.Msg)) { - sc.sendMsg = sendMsg -} - -func (sc *ScriptController) LoadScript(filename string) tea.Msg { - ctx := context.Background() - plugin, err := sc.scriptManager.LoadScript(ctx, filename) - if err != nil { - return events.Error(err) - } - - return events.StatusMsg(fmt.Sprintf("Script '%v' loaded", plugin.Name())) -} - -func (sc *ScriptController) RunScript(filename string) tea.Msg { - ctx := context.Background() - if err := sc.scriptManager.StartAdHocScript(ctx, filename, sc.waitAndPrintScriptError()); err != nil { - return events.Error(err) - } - return nil -} - -func (sc *ScriptController) waitAndPrintScriptError() chan error { - errChan := make(chan error) - go func() { - if err := <-errChan; err != nil { - sc.sendMsg(events.Error(err)) - } - }() - return errChan -} - -func (sc *ScriptController) LookupCommand(name string) commandctrl.Command { - cmd := sc.scriptManager.LookupCommand(name) - if cmd == nil { - return nil - } - - return func(execCtx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - errChan := sc.waitAndPrintScriptError() - ctx := context.Background() - - invokeArgs := make([]string, 0) - for args.NArgs() > 0 { - var s string - if err := args.Bind(&s); err == nil { - invokeArgs = append(invokeArgs, s) - } - } - - if err := cmd.Invoke(ctx, invokeArgs, errChan); err != nil { - return events.Error(err) - } - return nil - } -} - -type uiImpl struct { - sc *ScriptController -} - -func (u uiImpl) PrintMessage(ctx context.Context, msg string) { - u.sc.sendMsg(events.StatusMsg(msg)) -} - -func (u uiImpl) Prompt(ctx context.Context, msg string) chan string { - resultChan := make(chan string) - u.sc.sendMsg(events.PromptForInputMsg{ - Prompt: msg, - OnDone: func(value string) tea.Msg { - resultChan <- value - return nil - }, - OnCancel: func() tea.Msg { - close(resultChan) - return nil - }, - }) - return resultChan -} - -type sessionImpl struct { - sc *ScriptController - lastSelectedItemIndex int -} - -func (s *sessionImpl) subscribeToEvents(bus *bus.Bus) { - bus.On("ui.new-item-selected", func(rs *models.ResultSet, itemIndex int) { - s.lastSelectedItemIndex = itemIndex - }) -} - -func (s *sessionImpl) SelectedItemIndex(ctx context.Context) int { - return s.lastSelectedItemIndex -} - -func (s *sessionImpl) ResultSet(ctx context.Context) *models.ResultSet { - return s.sc.tableReadController.state.ResultSet() -} - -func (s *sessionImpl) SetResultSet(ctx context.Context, newResultSet *models.ResultSet) { - state := s.sc.tableReadController.state - msg := s.sc.tableReadController.setResultSetAndFilter(newResultSet, state.filter, true, resultSetUpdateScript) - s.sc.sendMsg(msg) -} - -func (s *sessionImpl) Query(ctx context.Context, query string, opts scriptmanager.QueryOptions) (*models.ResultSet, error) { - // Parse the query - expr, err := queryexpr.Parse(query) - if err != nil { - return nil, err - } - - if opts.NamePlaceholders != nil { - expr = expr.WithNameParams(opts.NamePlaceholders) - } - if opts.ValuePlaceholders != nil { - expr = expr.WithValueParams(opts.ValuePlaceholders) - } - if opts.IndexName != "" { - expr = expr.WithIndex(opts.IndexName) - } - - return s.sc.doQuery(ctx, expr, opts) -} - -func (s *ScriptController) doQuery(ctx context.Context, expr *queryexpr.QueryExpr, opts scriptmanager.QueryOptions) (*models.ResultSet, error) { - // Get the table info - var ( - tableInfo *models.TableInfo - err error - ) - - tableName := opts.TableName - currentResultSet := s.tableReadController.state.ResultSet() - - if tableName != "" { - // Table specified. If it's the same as the current table, then use the existing table info - if currentResultSet != nil && currentResultSet.TableInfo.Name == tableName { - tableInfo = currentResultSet.TableInfo - } - - // Otherwise, describe the table - tableInfo, err = s.tableReadController.tableService.Describe(ctx, tableName) - if err != nil { - return nil, errors.Wrapf(err, "cannot describe table '%v'", tableName) - } - } else { - // Table not specified. Use the existing table, if any - if currentResultSet == nil { - return nil, errors.New("no table currently selected") - } - tableInfo = currentResultSet.TableInfo - } - - newResultSet, err := s.tableReadController.tableService.ScanOrQuery(ctx, tableInfo, expr, nil) - if err != nil { - return nil, err - } - return newResultSet, nil -} - -func (sc *ScriptController) CustomKeyCommand(key string) tea.Cmd { - _, cmd := sc.scriptManager.LookupKeyBinding(key) - if cmd == nil { - return nil - } - - return func() tea.Msg { - errChan := sc.waitAndPrintScriptError() - ctx := context.Background() - - if err := cmd.Invoke(ctx, nil, errChan); err != nil { - return events.Error(err) - } - return nil - } -} - -func (sc *ScriptController) Rebind(bindingName string, newKey string) error { - return sc.scriptManager.RebindKeyBinding(bindingName, newKey) -} - -func (sc *ScriptController) LookupBinding(theKey string) string { - bindingName, _ := sc.scriptManager.LookupKeyBinding(theKey) - return bindingName -} - -func (sc *ScriptController) UnbindKey(key string) { - sc.scriptManager.UnbindKey(key) -} - -func (c *ScriptController) LookupRelatedItems(idx int) (res tea.Msg) { - rs := c.tableReadController.state.ResultSet() - - relItems, err := c.scriptManager.RelatedItemOfItem(context.Background(), rs, idx) - if err != nil { - return events.Error(err) - } else if len(relItems) == 0 { - return events.StatusMsg("No related items available") - } - - return ShowRelatedItemsOverlay{ - Items: relItems, - OnSelected: func(item relitems.RelatedItem) tea.Msg { - if item.OnSelect != nil { - return item.OnSelect() - } - - return NewJob(c.jobController, "Running query…", func(ctx context.Context) (*models.ResultSet, error) { - return c.doQuery(ctx, item.Query, scriptmanager.QueryOptions{ - TableName: item.Table, - }) - }).OnDone(func(rs *models.ResultSet) tea.Msg { - return c.tableReadController.setResultSetAndFilter(rs, "", true, resultSetUpdateQuery) - }).Submit() - }, - } -} diff --git a/internal/dynamo-browse/controllers/scripts_test.go b/internal/dynamo-browse/controllers/scripts_test.go deleted file mode 100644 index ffcb8a8..0000000 --- a/internal/dynamo-browse/controllers/scripts_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package controllers_test - -import ( - "testing" - "time" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/common/ui/events" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers" - "github.com/stretchr/testify/assert" -) - -func TestScriptController_RunScript(t *testing.T) { - t.Run("should execute scripts successfully", func(t *testing.T) { - srv := newService(t, serviceConfig{ - scriptFS: testScriptFile(t, "test.tm", ` - ui.print("Hello world") - `), - }) - - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg("Hello world"), srv.msgSender.msgs[0]) - }) - - t.Run("session.result_set", func(t *testing.T) { - t.Run("should return current result set if not-nil", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - rs := session.result_set() - ui.print(rs.length) - `), - }) - - invokeCommand(t, srv.readController.Init()) - - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg("3"), srv.msgSender.msgs[0]) - }) - }) - - t.Run("session.query", func(t *testing.T) { - t.Run("should run query against current table", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - rs := session.query('pk="abc"') - ui.print(rs.length) - `), - }) - - invokeCommand(t, srv.readController.Init()) - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg("2"), srv.msgSender.msgs[0]) - }) - - t.Run("should run query against another table", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - rs := session.query('pk!="abc"', { table: "count-to-30" }) - ui.print(rs.length) - `), - }) - - invokeCommand(t, srv.readController.Init()) - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg("30"), srv.msgSender.msgs[0]) - }) - }) - - t.Run("session.set_result_set", func(t *testing.T) { - t.Run("should set the result set from the result of a query", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - rs := session.query('pk="abc"') - session.set_result_set(rs) - `), - }) - - invokeCommand(t, srv.readController.Init()) - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.IsType(t, controllers.NewResultSet{}, srv.msgSender.msgs[0]) - }) - - t.Run("changed attributes of the result set should show up as modified", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - rs := session.query('pk="abc"') - rs[0].set_attr("pk", "131") - session.set_result_set(rs) - `), - }) - - invokeCommand(t, srv.readController.Init()) - msg := srv.scriptController.RunScript("test.tm") - assert.Nil(t, msg) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.IsType(t, controllers.NewResultSet{}, srv.msgSender.msgs[0]) - - assert.Equal(t, "131", srv.state.ResultSet().Items()[0]["pk"].(*types.AttributeValueMemberS).Value) - assert.True(t, srv.state.ResultSet().IsDirty(0)) - }) - }) -} - -func TestScriptController_LookupCommand(t *testing.T) { - t.Run("should schedule the script on a separate go-routine", func(t *testing.T) { - scenarios := []struct { - descr string - command string - expectedOutput string - }{ - {descr: "command with arg", command: "mycommand \"test name\"", expectedOutput: "Hello, test name"}, - {descr: "command no arg", command: "mycommand", expectedOutput: "Hello, nil value"}, - } - - for _, scenario := range scenarios { - t.Run(scenario.descr, func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - ext.command("mycommand", func(name = "nil value") { - ui.print(sprintf("Hello, %v", name)) - }) - `), - }) - - invokeCommand(t, srv.scriptController.LoadScript("test.tm")) - invokeCommand(t, srv.commandController.Execute(scenario.command)) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg(scenario.expectedOutput), srv.msgSender.msgs[0]) - }) - } - }) - - t.Run("should only allow one script to run at a time", func(t *testing.T) { - srv := newService(t, serviceConfig{ - tableName: "alpha-table", - scriptFS: testScriptFile(t, "test.tm", ` - ext.command("mycommand", func() { - time.sleep(1.5) - ui.print("Done my thing") - }) - `), - }) - - invokeCommand(t, srv.scriptController.LoadScript("test.tm")) - - invokeCommand(t, srv.commandController.Execute(`mycommand`)) - invokeCommandExpectingError(t, srv.commandController.Execute(`mycommand`)) - - srv.msgSender.waitForAtLeastOneMessages(t, 5*time.Second) - - assert.Len(t, srv.msgSender.msgs, 1) - assert.Equal(t, events.StatusMsg("Done my thing"), srv.msgSender.msgs[0]) - }) - -} diff --git a/internal/dynamo-browse/controllers/tableread.go b/internal/dynamo-browse/controllers/tableread.go index 38946f3..64aca40 100644 --- a/internal/dynamo-browse/controllers/tableread.go +++ b/internal/dynamo-browse/controllers/tableread.go @@ -61,7 +61,6 @@ type TableReadController struct { tableName string loadFromLastView bool pasteboardProvider services.PasteboardProvider - relatedItemSupplier RelatedItemSupplier // state mutex *sync.Mutex @@ -77,7 +76,6 @@ func NewTableReadController( inputHistoryService *inputhistory.Service, eventBus *bus.Bus, pasteboardProvider services.PasteboardProvider, - relatedItemSupplier RelatedItemSupplier, tableName string, ) *TableReadController { return &TableReadController{ @@ -90,7 +88,6 @@ func NewTableReadController( eventBus: eventBus, tableName: tableName, pasteboardProvider: pasteboardProvider, - relatedItemSupplier: relatedItemSupplier, mutex: new(sync.Mutex), } } diff --git a/internal/dynamo-browse/controllers/tablewrite_test.go b/internal/dynamo-browse/controllers/tablewrite_test.go index a407923..0ace3d9 100644 --- a/internal/dynamo-browse/controllers/tablewrite_test.go +++ b/internal/dynamo-browse/controllers/tablewrite_test.go @@ -15,7 +15,6 @@ import ( "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/inputhistory" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables" "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/viewsnapshot" "github.com/lmika/dynamo-browse/test/testdynamo" @@ -587,7 +586,6 @@ type services struct { settingsController *controllers.SettingsController columnsController *controllers.ColumnsController exportController *controllers.ExportController - scriptController *controllers.ScriptController commandController *commandctrl.CommandController } @@ -607,7 +605,6 @@ func newService(t *testing.T, cfg serviceConfig) *services { workspaceService := viewsnapshot.NewService(resultSetSnapshotStore) itemRendererService := itemrenderer.NewService(itemrenderer.PlainTextRenderer(), itemrenderer.PlainTextRenderer()) - scriptService := scriptmanager.New() inputHistoryService := inputhistory.New(inputHistoryStore) client := testdynamo.SetupTestTable(t, testData) @@ -627,17 +624,14 @@ func newService(t *testing.T, cfg serviceConfig) *services { inputHistoryService, eventBus, pasteboardprovider.NilProvider{}, - nil, cfg.tableName, ) writeController := controllers.NewTableWriteController(state, service, jobsController, readController, settingStore) settingsController := controllers.NewSettingsController(settingStore, eventBus) columnsController := controllers.NewColumnsController(readController, eventBus) exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{}) - scriptController := controllers.NewScriptController(scriptService, readController, jobsController, settingsController, eventBus) commandController, _ := commandctrl.NewCommandController(inputHistoryService) - commandController.AddCommandLookupExtension(scriptController) if cfg.isReadOnly { if err := settingStore.SetReadOnly(cfg.isReadOnly); err != nil { @@ -651,12 +645,7 @@ func newService(t *testing.T, cfg serviceConfig) *services { } msgSender := &msgSender{} - scriptController.Init() jobsController.SetMessageSender(msgSender.send) - scriptController.SetMessageSender(msgSender.send) - - // Initting will setup the default script lookup paths, so revert them to the test ones - scriptService.SetLookupPaths([]fs.FS{cfg.scriptFS}) return &services{ state: state, @@ -666,7 +655,6 @@ func newService(t *testing.T, cfg serviceConfig) *services { settingsController: settingsController, columnsController: columnsController, exportController: exportController, - scriptController: scriptController, commandController: commandController, msgSender: msgSender, } diff --git a/internal/dynamo-browse/services/scriptmanager/builtins.go b/internal/dynamo-browse/services/scriptmanager/builtins.go deleted file mode 100644 index 93c6e78..0000000 --- a/internal/dynamo-browse/services/scriptmanager/builtins.go +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Builtins adopted and modified from Taramin - * Copyright (c) 2022 Curtis Myzie - */ - -package scriptmanager - -import ( - "context" - "fmt" - "log" - - "github.com/pkg/errors" - "github.com/risor-io/risor/object" -) - -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 -} - -// This is taken from the args package -func require(funcName string, count int, args []object.Object) *object.Error { - nArgs := len(args) - if nArgs != count { - if count == 1 { - return object.Errorf( - fmt.Sprintf("type error: %s() takes exactly 1 argument (%d given)", - funcName, nArgs)) - } - return object.Errorf( - fmt.Sprintf("type error: %s() takes exactly %d arguments (%d given)", - funcName, count, nArgs)) - } - return nil -} - -func bindArgs(funcName string, args []object.Object, bindArgs ...any) *object.Error { - if err := require(funcName, len(bindArgs), args); err != nil { - return err - } - - for i, bindArg := range bindArgs { - switch t := bindArg.(type) { - case *string: - str, err := object.AsString(args[i]) - if err != nil { - return err - } - - *t = str - case **object.Function: - fnRes, isFnRes := args[i].(*object.Function) - if !isFnRes { - return object.NewError(errors.Errorf("expected arg %v to be a function, was %T", i, bindArg)) - } - - *t = fnRes - default: - return object.NewError(errors.Errorf("unhandled arg type %v", i)) - } - } - return nil -} diff --git a/internal/dynamo-browse/services/scriptmanager/iface.go b/internal/dynamo-browse/services/scriptmanager/iface.go deleted file mode 100644 index 39b4d9e..0000000 --- a/internal/dynamo-browse/services/scriptmanager/iface.go +++ /dev/null @@ -1,38 +0,0 @@ -package scriptmanager - -import ( - "context" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" -) - -//go:generate mockery --with-expecter --name UIService -//go:generate mockery --with-expecter --name SessionService - -type Ifaces struct { - UI UIService - Session SessionService -} - -type UIService interface { - PrintMessage(ctx context.Context, msg string) - - // Prompt should return a channel which will provide the input from the user. If the user - // provides no input, prompt should close the channel without providing anything. - Prompt(ctx context.Context, msg string) chan string -} - -type SessionService interface { - Query(ctx context.Context, expr string, queryOptions QueryOptions) (*models.ResultSet, error) - - ResultSet(ctx context.Context) *models.ResultSet - SelectedItemIndex(ctx context.Context) int - SetResultSet(ctx context.Context, newResultSet *models.ResultSet) -} - -type QueryOptions struct { - TableName string - IndexName string - NamePlaceholders map[string]string - ValuePlaceholders map[string]types.AttributeValue -} diff --git a/internal/dynamo-browse/services/scriptmanager/mocks/SessionService.go b/internal/dynamo-browse/services/scriptmanager/mocks/SessionService.go deleted file mode 100644 index bdfa6b1..0000000 --- a/internal/dynamo-browse/services/scriptmanager/mocks/SessionService.go +++ /dev/null @@ -1,216 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - models "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - mock "github.com/stretchr/testify/mock" - - scriptmanager "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" -) - -// SessionService is an autogenerated mock type for the SessionService type -type SessionService struct { - mock.Mock -} - -type SessionService_Expecter struct { - mock *mock.Mock -} - -func (_m *SessionService) EXPECT() *SessionService_Expecter { - return &SessionService_Expecter{mock: &_m.Mock} -} - -// Query provides a mock function with given fields: ctx, expr, queryOptions -func (_m *SessionService) Query(ctx context.Context, expr string, queryOptions scriptmanager.QueryOptions) (*models.ResultSet, error) { - ret := _m.Called(ctx, expr, queryOptions) - - var r0 *models.ResultSet - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, scriptmanager.QueryOptions) (*models.ResultSet, error)); ok { - return rf(ctx, expr, queryOptions) - } - if rf, ok := ret.Get(0).(func(context.Context, string, scriptmanager.QueryOptions) *models.ResultSet); ok { - r0 = rf(ctx, expr, queryOptions) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.ResultSet) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, scriptmanager.QueryOptions) error); ok { - r1 = rf(ctx, expr, queryOptions) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SessionService_Query_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Query' -type SessionService_Query_Call struct { - *mock.Call -} - -// Query is a helper method to define mock.On call -// - ctx context.Context -// - expr string -// - queryOptions scriptmanager.QueryOptions -func (_e *SessionService_Expecter) Query(ctx interface{}, expr interface{}, queryOptions interface{}) *SessionService_Query_Call { - return &SessionService_Query_Call{Call: _e.mock.On("Query", ctx, expr, queryOptions)} -} - -func (_c *SessionService_Query_Call) Run(run func(ctx context.Context, expr string, queryOptions scriptmanager.QueryOptions)) *SessionService_Query_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(scriptmanager.QueryOptions)) - }) - return _c -} - -func (_c *SessionService_Query_Call) Return(_a0 *models.ResultSet, _a1 error) *SessionService_Query_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *SessionService_Query_Call) RunAndReturn(run func(context.Context, string, scriptmanager.QueryOptions) (*models.ResultSet, error)) *SessionService_Query_Call { - _c.Call.Return(run) - return _c -} - -// ResultSet provides a mock function with given fields: ctx -func (_m *SessionService) ResultSet(ctx context.Context) *models.ResultSet { - ret := _m.Called(ctx) - - var r0 *models.ResultSet - if rf, ok := ret.Get(0).(func(context.Context) *models.ResultSet); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.ResultSet) - } - } - - return r0 -} - -// SessionService_ResultSet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ResultSet' -type SessionService_ResultSet_Call struct { - *mock.Call -} - -// ResultSet is a helper method to define mock.On call -// - ctx context.Context -func (_e *SessionService_Expecter) ResultSet(ctx interface{}) *SessionService_ResultSet_Call { - return &SessionService_ResultSet_Call{Call: _e.mock.On("ResultSet", ctx)} -} - -func (_c *SessionService_ResultSet_Call) Run(run func(ctx context.Context)) *SessionService_ResultSet_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *SessionService_ResultSet_Call) Return(_a0 *models.ResultSet) *SessionService_ResultSet_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *SessionService_ResultSet_Call) RunAndReturn(run func(context.Context) *models.ResultSet) *SessionService_ResultSet_Call { - _c.Call.Return(run) - return _c -} - -// SelectedItemIndex provides a mock function with given fields: ctx -func (_m *SessionService) SelectedItemIndex(ctx context.Context) int { - ret := _m.Called(ctx) - - var r0 int - if rf, ok := ret.Get(0).(func(context.Context) int); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(int) - } - - return r0 -} - -// SessionService_SelectedItemIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SelectedItemIndex' -type SessionService_SelectedItemIndex_Call struct { - *mock.Call -} - -// SelectedItemIndex is a helper method to define mock.On call -// - ctx context.Context -func (_e *SessionService_Expecter) SelectedItemIndex(ctx interface{}) *SessionService_SelectedItemIndex_Call { - return &SessionService_SelectedItemIndex_Call{Call: _e.mock.On("SelectedItemIndex", ctx)} -} - -func (_c *SessionService_SelectedItemIndex_Call) Run(run func(ctx context.Context)) *SessionService_SelectedItemIndex_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *SessionService_SelectedItemIndex_Call) Return(_a0 int) *SessionService_SelectedItemIndex_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *SessionService_SelectedItemIndex_Call) RunAndReturn(run func(context.Context) int) *SessionService_SelectedItemIndex_Call { - _c.Call.Return(run) - return _c -} - -// SetResultSet provides a mock function with given fields: ctx, newResultSet -func (_m *SessionService) SetResultSet(ctx context.Context, newResultSet *models.ResultSet) { - _m.Called(ctx, newResultSet) -} - -// SessionService_SetResultSet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetResultSet' -type SessionService_SetResultSet_Call struct { - *mock.Call -} - -// SetResultSet is a helper method to define mock.On call -// - ctx context.Context -// - newResultSet *models.ResultSet -func (_e *SessionService_Expecter) SetResultSet(ctx interface{}, newResultSet interface{}) *SessionService_SetResultSet_Call { - return &SessionService_SetResultSet_Call{Call: _e.mock.On("SetResultSet", ctx, newResultSet)} -} - -func (_c *SessionService_SetResultSet_Call) Run(run func(ctx context.Context, newResultSet *models.ResultSet)) *SessionService_SetResultSet_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*models.ResultSet)) - }) - return _c -} - -func (_c *SessionService_SetResultSet_Call) Return() *SessionService_SetResultSet_Call { - _c.Call.Return() - return _c -} - -func (_c *SessionService_SetResultSet_Call) RunAndReturn(run func(context.Context, *models.ResultSet)) *SessionService_SetResultSet_Call { - _c.Call.Return(run) - return _c -} - -type mockConstructorTestingTNewSessionService interface { - mock.TestingT - Cleanup(func()) -} - -// NewSessionService creates a new instance of SessionService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSessionService(t mockConstructorTestingTNewSessionService) *SessionService { - mock := &SessionService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/dynamo-browse/services/scriptmanager/mocks/UIService.go b/internal/dynamo-browse/services/scriptmanager/mocks/UIService.go deleted file mode 100644 index b029dd6..0000000 --- a/internal/dynamo-browse/services/scriptmanager/mocks/UIService.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" -) - -// UIService is an autogenerated mock type for the UIService type -type UIService struct { - mock.Mock -} - -type UIService_Expecter struct { - mock *mock.Mock -} - -func (_m *UIService) EXPECT() *UIService_Expecter { - return &UIService_Expecter{mock: &_m.Mock} -} - -// PrintMessage provides a mock function with given fields: ctx, msg -func (_m *UIService) PrintMessage(ctx context.Context, msg string) { - _m.Called(ctx, msg) -} - -// UIService_PrintMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrintMessage' -type UIService_PrintMessage_Call struct { - *mock.Call -} - -// PrintMessage is a helper method to define mock.On call -// - ctx context.Context -// - msg string -func (_e *UIService_Expecter) PrintMessage(ctx interface{}, msg interface{}) *UIService_PrintMessage_Call { - return &UIService_PrintMessage_Call{Call: _e.mock.On("PrintMessage", ctx, msg)} -} - -func (_c *UIService_PrintMessage_Call) Run(run func(ctx context.Context, msg string)) *UIService_PrintMessage_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *UIService_PrintMessage_Call) Return() *UIService_PrintMessage_Call { - _c.Call.Return() - return _c -} - -func (_c *UIService_PrintMessage_Call) RunAndReturn(run func(context.Context, string)) *UIService_PrintMessage_Call { - _c.Call.Return(run) - return _c -} - -// Prompt provides a mock function with given fields: ctx, msg -func (_m *UIService) Prompt(ctx context.Context, msg string) chan string { - ret := _m.Called(ctx, msg) - - var r0 chan string - if rf, ok := ret.Get(0).(func(context.Context, string) chan string); ok { - r0 = rf(ctx, msg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(chan string) - } - } - - return r0 -} - -// UIService_Prompt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Prompt' -type UIService_Prompt_Call struct { - *mock.Call -} - -// Prompt is a helper method to define mock.On call -// - ctx context.Context -// - msg string -func (_e *UIService_Expecter) Prompt(ctx interface{}, msg interface{}) *UIService_Prompt_Call { - return &UIService_Prompt_Call{Call: _e.mock.On("Prompt", ctx, msg)} -} - -func (_c *UIService_Prompt_Call) Run(run func(ctx context.Context, msg string)) *UIService_Prompt_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *UIService_Prompt_Call) Return(_a0 chan string) *UIService_Prompt_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *UIService_Prompt_Call) RunAndReturn(run func(context.Context, string) chan string) *UIService_Prompt_Call { - _c.Call.Return(run) - return _c -} - -type mockConstructorTestingTNewUIService interface { - mock.TestingT - Cleanup(func()) -} - -// NewUIService creates a new instance of UIService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewUIService(t mockConstructorTestingTNewUIService) *UIService { - mock := &UIService{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/dynamo-browse/services/scriptmanager/modext.go b/internal/dynamo-browse/services/scriptmanager/modext.go deleted file mode 100644 index 4ad97d4..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modext.go +++ /dev/null @@ -1,270 +0,0 @@ -package scriptmanager - -import ( - "context" - "fmt" - "regexp" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr" - "github.com/pkg/errors" - "github.com/risor-io/risor/object" -) - -var ( - validKeyBindingNames = regexp.MustCompile(`^[-a-zA-Z0-9_]+$`) -) - -type extModule struct { - scriptPlugin *ScriptPlugin -} - -func (m *extModule) register() *object.Module { - return object.NewBuiltinsModule("ext", map[string]object.Object{ - "command": object.NewBuiltin("command", m.command), - "key_binding": object.NewBuiltin("key_binding", m.keyBinding), - "related_items": object.NewBuiltin("related_items", m.relatedItem), - }) -} - -func (m *extModule) command(ctx context.Context, args ...object.Object) object.Object { - thisEnv := scriptEnvFromCtx(ctx) - - if err := require("ext.command", 2, args); err != nil { - return err - } - - cmdName, err := object.AsString(args[0]) - if err != nil { - return err - } - fnRes, isFnRes := args[1].(*object.Function) - if !isFnRes { - return object.NewError(errors.New("expected second arg to be a function")) - } - - callFn, hasCallFn := object.GetCallFunc(ctx) - if !hasCallFn { - return object.NewError(errors.New("no callFn found in context")) - } - - // This command function will be executed by the script scheduler - newCommand := func(ctx context.Context, args []string) error { - objArgs := make([]object.Object, len(args)) - for i, a := range args { - objArgs[i] = object.NewString(a) - } - - newEnv := thisEnv - ctx = ctxWithScriptEnv(ctx, newEnv) - - res, err := callFn(ctx, fnRes, objArgs) - if err != nil { - return errors.Errorf("command error '%v':%v - %v", m.scriptPlugin.name, cmdName, err) - } else if object.IsError(res) { - errObj := res.(*object.Error) - return errors.Errorf("command error '%v':%v - %v", m.scriptPlugin.name, cmdName, errObj.Inspect()) - } - return nil - } - - if m.scriptPlugin.definedCommands == nil { - m.scriptPlugin.definedCommands = make(map[string]*Command) - } - m.scriptPlugin.definedCommands[cmdName] = &Command{plugin: m.scriptPlugin, cmdFn: newCommand} - return nil -} - -func (m *extModule) keyBinding(ctx context.Context, args ...object.Object) object.Object { - thisEnv := scriptEnvFromCtx(ctx) - - if err := require("ext.key_binding", 3, args); err != nil { - return err - } - - bindingName, err := object.AsString(args[0]) - if err != nil { - return err - } else if !validKeyBindingNames.MatchString(bindingName) { - return object.NewError(errors.New("value error: binding name must match regexp [-a-zA-Z0-9_]+")) - } - - options, err := object.AsMap(args[1]) - if err != nil { - return err - } - - var defaultKey string - if strVal, isStrVal := options.Get("default").(*object.String); isStrVal { - defaultKey = strVal.Value() - } - - fnRes, isFnRes := args[2].(*object.Function) - if !isFnRes { - return object.NewError(errors.New("expected second arg to be a function")) - } - - callFn, hasCallFn := object.GetCallFunc(ctx) - if !hasCallFn { - return object.NewError(errors.New("no callFn found in context")) - } - - // This command function will be executed by the script scheduler - newCommand := func(ctx context.Context, args []string) error { - objArgs := make([]object.Object, len(args)) - for i, a := range args { - objArgs[i] = object.NewString(a) - } - - newEnv := thisEnv - ctx = ctxWithScriptEnv(ctx, newEnv) - - res, err := callFn(ctx, fnRes, objArgs) - if err != nil { - return errors.Errorf("command error '%v':%v - %v", m.scriptPlugin.name, bindingName, err) - } else if object.IsError(res) { - errObj := res.(*object.Error) - return errors.Errorf("command error '%v':%v - %v", m.scriptPlugin.name, bindingName, errObj.Inspect()) - } - return nil - } - - fullBindingName := fmt.Sprintf("ext.%v.%v", m.scriptPlugin.name, bindingName) - - if m.scriptPlugin.definedKeyBindings == nil { - m.scriptPlugin.definedKeyBindings = make(map[string]*Command) - m.scriptPlugin.keyToKeyBinding = make(map[string]string) - } - - m.scriptPlugin.definedKeyBindings[fullBindingName] = &Command{plugin: m.scriptPlugin, cmdFn: newCommand} - m.scriptPlugin.keyToKeyBinding[defaultKey] = fullBindingName - return nil -} - -func (m *extModule) relatedItem(ctx context.Context, args ...object.Object) object.Object { - thisEnv := scriptEnvFromCtx(ctx) - - var ( - tableName string - callbackFn *object.Function - ) - if err := bindArgs("ext.related_items", args, &tableName, &callbackFn); err != nil { - return err - } - - callFn, hasCallFn := object.GetCallFunc(ctx) - if !hasCallFn { - return object.NewError(errors.New("no callFn found in context")) - } - - newHandler := func(ctx context.Context, rs *models.ResultSet, index int) ([]relatedItem, error) { - newEnv := thisEnv - ctx = ctxWithScriptEnv(ctx, newEnv) - - res, err := callFn(ctx, callbackFn, []object.Object{ - newItemProxy(newResultSetProxy(rs), index), - }) - - if err != nil { - return nil, errors.Errorf("script error '%v':related_item - %v", m.scriptPlugin.name, err) - } else if object.IsError(res) { - errObj := res.(*object.Error) - return nil, errors.Errorf("script error '%v':related_item - %v", m.scriptPlugin.name, errObj.Inspect()) - } - - itr, objErr := object.AsIterator(res) - if err != nil { - return nil, objErr.Value() - } - - var relItems []relatedItem - for next, hasNext := itr.Next(ctx); hasNext; next, hasNext = itr.Next(ctx) { - var newRelItem relatedItem - - itemMap, objErr := object.AsMap(next) - if err != nil { - return nil, objErr.Value() - } - - labelName, objErr := object.AsString(itemMap.Get("label")) - if objErr != nil { - continue - } - newRelItem.label = labelName - - var tableStr = "" - if itemMap.Get("table") != object.Nil { - tableStr, objErr = object.AsString(itemMap.Get("table")) - if objErr != nil { - continue - } - } - newRelItem.table = tableStr - - if selectFn, ok := itemMap.Get("on_select").(*object.Function); ok { - newRelItem.onSelect = func() error { - thisNewEnv := thisEnv - ctx = ctxWithScriptEnv(ctx, thisNewEnv) - - res, err := callFn(ctx, selectFn, []object.Object{}) - if err != nil { - return errors.Errorf("rel error '%v' - %v", m.scriptPlugin.name, err) - } else if object.IsError(res) { - errObj := res.(*object.Error) - return errors.Errorf("rel error '%v' - %v", m.scriptPlugin.name, errObj.Inspect()) - } - return nil - } - } else { - queryExprStr, objErr := object.AsString(itemMap.Get("query")) - if objErr != nil { - continue - } - - query, err := queryexpr.Parse(queryExprStr) - if err != nil { - continue - } - - // Placeholders - if argsVal, isArgsValMap := object.AsMap(itemMap.Get("args")); isArgsValMap == nil { - namePlaceholders := make(map[string]string) - valuePlaceholders := make(map[string]types.AttributeValue) - - for k, val := range argsVal.Value() { - switch v := val.(type) { - case *object.String: - namePlaceholders[k] = v.Value() - valuePlaceholders[k] = &types.AttributeValueMemberS{Value: v.Value()} - case *object.Int: - valuePlaceholders[k] = &types.AttributeValueMemberN{Value: fmt.Sprint(v.Value())} - case *object.Float: - valuePlaceholders[k] = &types.AttributeValueMemberN{Value: fmt.Sprint(v.Value())} - case *object.Bool: - valuePlaceholders[k] = &types.AttributeValueMemberBOOL{Value: v.Value()} - case *object.NilType: - valuePlaceholders[k] = &types.AttributeValueMemberNULL{Value: true} - default: - continue - } - } - - query = query.WithNameParams(namePlaceholders).WithValueParams(valuePlaceholders) - } - newRelItem.query = query - } - - relItems = append(relItems, newRelItem) - } - - return relItems, nil - } - - m.scriptPlugin.relatedItems = append(m.scriptPlugin.relatedItems, &relatedItemBuilder{ - table: tableName, - itemProduction: newHandler, - }) - - return nil -} diff --git a/internal/dynamo-browse/services/scriptmanager/modext_test.go b/internal/dynamo-browse/services/scriptmanager/modext_test.go deleted file mode 100644 index b3413e6..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modext_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/stretchr/testify/assert" -) - -func TestExtModule_RelatedItems(t *testing.T) { - t.Run("should register a function which will return related items for an item", func(t *testing.T) { - scenarios := []struct { - desc string - code string - }{ - { - desc: "single function, table name match", - code: ` - ext.related_items("test-table", func(item) { - print("Hello") - return [ - {"label": "Customer", "query": "pk=$foo", "args": {"foo": "foo"}}, - {"label": "Payment", "query": "fla=$daa", "args": {"daa": "Hello"}}, - ] - }) - `, - }, - { - desc: "single function, table prefix match", - code: ` - ext.related_items("test-*", func(item) { - print("Hello") - return [ - {"label": "Customer", "query": "pk=$foo", "args": {"foo": "foo"}}, - {"label": "Payment", "query": "fla=$daa", "args": {"daa": "Hello"}}, - ] - }) - `, - }, - { - desc: "multi function, table name match", - code: ` - ext.related_items("test-table", func(item) { - print("Hello") - return [ - {"label": "Customer", "query": "pk=$foo", "args": {"foo": "foo"}}, - ] - }) - - ext.related_items("test-table", func(item) { - return [ - {"label": "Payment", "query": "fla=$daa", "args": {"daa": "Hello"}}, - ] - }) - `, - }, - { - desc: "multi function, table name prefix", - code: ` - ext.related_items("test-*", func(item) { - print("Hello") - return [ - {"label": "Customer", "query": "pk=$foo", "args": {"foo": "foo"}}, - ] - }) - - ext.related_items("test-*", func(item) { - return [ - {"label": "Payment", "query": "fla=$daa", "args": {"daa": "Hello"}}, - ] - }) - `, - }, - } - - for _, scenario := range scenarios { - t.Run(scenario.desc, func(t *testing.T) { - // Load the script - srv := scriptmanager.New(scriptmanager.WithFS(testScriptFile(t, "test.tm", scenario.code))) - - ctx := context.Background() - plugin, err := srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - assert.NotNil(t, plugin) - - // Get related items of result set - rs := &models.ResultSet{ - TableInfo: &models.TableInfo{ - Name: "test-table", - }, - } - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - relItems, err := srv.RelatedItemOfItem(context.Background(), rs, 0) - assert.NoError(t, err) - assert.Len(t, relItems, 2) - - assert.Equal(t, "Customer", relItems[0].Name) - assert.Equal(t, "pk=$foo", relItems[0].Query.String()) - assert.Equal(t, "foo", relItems[0].Query.ValueParamOrNil("foo").(*types.AttributeValueMemberS).Value) - - assert.Equal(t, "Payment", relItems[1].Name) - assert.Equal(t, "fla=$daa", relItems[1].Query.String()) - assert.Equal(t, "Hello", relItems[1].Query.ValueParamOrNil("daa").(*types.AttributeValueMemberS).Value) - }) - } - }) - - t.Run("should support rel_items with on select", func(t *testing.T) { - // Load the script - srv := scriptmanager.New(scriptmanager.WithFS(testScriptFile(t, "test.tm", ` - ext.related_items("test-table", func(item) { - print("Hello") - return [ - {"label": "Customer", "on_select": func() { - print("Selected") - }}, - ] - }) - `))) - - ctx := context.Background() - plugin, err := srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - assert.NotNil(t, plugin) - - // Get related items of result set - rs := &models.ResultSet{ - TableInfo: &models.TableInfo{ - Name: "test-table", - }, - } - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - relItems, err := srv.RelatedItemOfItem(context.Background(), rs, 0) - assert.NoError(t, err) - assert.Len(t, relItems, 1) - - assert.Equal(t, "Customer", relItems[0].Name) - assert.NoError(t, relItems[0].OnSelect()) - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modos_test.go b/internal/dynamo-browse/services/scriptmanager/modos_test.go deleted file mode 100644 index 455125b..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modos_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager/mocks" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "testing" -) - -func TestOSModule_Env(t *testing.T) { - t.Run("should return value of environment variables", func(t *testing.T) { - t.Setenv("FULL_VALUE", "this is a value") - t.Setenv("EMPTY_VALUE", "") - - testFS := testScriptFile(t, "test.tm", ` - assert(os.getenv("FULL_VALUE") == "this is a value") - assert(os.getenv("EMPTY_VALUE") == "") - assert(os.getenv("MISSING_VALUE") == "") - - assert(bool(os.getenv("FULL_VALUE")) == true) - assert(bool(os.getenv("EMPTY_VALUE")) == false) - assert(bool(os.getenv("MISSING_VALUE")) == false) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - }) -} - -func TestOSModule_Exec(t *testing.T) { - t.Run("should run command and return stdout", func(t *testing.T) { - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "hello world\n") - - testFS := testScriptFile(t, "test.tm", ` - res := exec('echo', ["hello world"]).stdout - ui.print(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modsession.go b/internal/dynamo-browse/services/scriptmanager/modsession.go deleted file mode 100644 index 95c16c7..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modsession.go +++ /dev/null @@ -1,145 +0,0 @@ -package scriptmanager - -import ( - "context" - "fmt" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/pkg/errors" - "github.com/risor-io/risor/object" -) - -type sessionModule struct { - sessionService SessionService -} - -func (um *sessionModule) query(ctx context.Context, args ...object.Object) object.Object { - if len(args) == 0 || len(args) > 2 { - return object.Errorf("type error: session.query takes either 1 or 2 arguments (%d given)", len(args)) - } - - var options QueryOptions - - expr, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - if len(args) == 2 { - objMap, objErr := object.AsMap(args[1]) - if objErr != nil { - return objErr - } - - // Table name - if val := objMap.Get("table"); val != object.Nil && val.IsTruthy() { - switch tv := val.(type) { - case *object.String: - options.TableName = tv.Value() - case *tableProxy: - options.TableName = tv.table.Name - default: - return object.Errorf("type error: query option 'table' must be either a string or table") - } - } - - // Index name - if val, isStr := objMap.Get("index").(*object.String); isStr { - options.IndexName = val.Value() - } - - // Placeholders - if argsVal, isArgsValMap := objMap.Get("args").(*object.Map); isArgsValMap { - options.NamePlaceholders = make(map[string]string) - options.ValuePlaceholders = make(map[string]types.AttributeValue) - - for k, val := range argsVal.Value() { - switch v := val.(type) { - case *object.String: - options.NamePlaceholders[k] = v.Value() - options.ValuePlaceholders[k] = &types.AttributeValueMemberS{Value: v.Value()} - case *object.Int: - options.ValuePlaceholders[k] = &types.AttributeValueMemberN{Value: fmt.Sprint(v.Value())} - case *object.Float: - options.ValuePlaceholders[k] = &types.AttributeValueMemberN{Value: fmt.Sprint(v.Value())} - case *object.Bool: - options.ValuePlaceholders[k] = &types.AttributeValueMemberBOOL{Value: v.Value()} - case *object.NilType: - options.ValuePlaceholders[k] = &types.AttributeValueMemberNULL{Value: true} - default: - return object.Errorf("type error: arg '%v' of type '%v' is not supported", k, val.Type()) - } - } - } - } - - resp, err := um.sessionService.Query(ctx, expr, options) - - if err != nil { - return object.NewError(err) - } - return &resultSetProxy{resultSet: resp} -} - -func (um *sessionModule) resultSet(ctx context.Context, args ...object.Object) object.Object { - if err := require("session.result_set", 0, args); err != nil { - return err - } - - rs := um.sessionService.ResultSet(ctx) - if rs == nil { - return object.Nil - } - return &resultSetProxy{resultSet: rs} -} - -func (um *sessionModule) selectedItem(ctx context.Context, args ...object.Object) object.Object { - if err := require("session.result_set", 0, args); err != nil { - return err - } - - rs := um.sessionService.ResultSet(ctx) - idx := um.sessionService.SelectedItemIndex(ctx) - if rs == nil || idx < 0 { - return object.Nil - } - - rsProxy := &resultSetProxy{resultSet: rs} - return newItemProxy(rsProxy, idx) -} - -func (um *sessionModule) setResultSet(ctx context.Context, args ...object.Object) object.Object { - if err := require("session.set_result_set", 1, args); err != nil { - return err - } - - resultSetProxy, isResultSetProxy := args[0].(*resultSetProxy) - if !isResultSetProxy { - return object.NewError(errors.Errorf("type error: expected a resultsset (got %v)", args[0])) - } - - um.sessionService.SetResultSet(ctx, resultSetProxy.resultSet) - return nil -} - -func (um *sessionModule) currentTable(ctx context.Context, args ...object.Object) object.Object { - if err := require("session.current_table", 0, args); err != nil { - return err - } - - rs := um.sessionService.ResultSet(ctx) - if rs == nil { - return object.Nil - } - - return &tableProxy{table: rs.TableInfo} -} - -func (um *sessionModule) register() *object.Module { - return object.NewBuiltinsModule("session", map[string]object.Object{ - "query": object.NewBuiltin("query", um.query), - "current_table": object.NewBuiltin("current_table", um.currentTable), - "result_set": object.NewBuiltin("result_set", um.resultSet), - "selected_item": object.NewBuiltin("selected_item", um.selectedItem), - "set_result_set": object.NewBuiltin("set_result_set", um.setResultSet), - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modsession_test.go b/internal/dynamo-browse/services/scriptmanager/modsession_test.go deleted file mode 100644 index 8712cf5..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modsession_test.go +++ /dev/null @@ -1,426 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager/mocks" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "testing" -) - -func TestModSession_Table(t *testing.T) { - t.Run("should return details of the current table", func(t *testing.T) { - tableDef := models.TableInfo{ - Name: "test_table", - Keys: models.KeyAttribute{ - PartitionKey: "pk", - SortKey: "sk", - }, - GSIs: []models.TableGSI{ - { - Name: "index-1", - Keys: models.KeyAttribute{ - PartitionKey: "ipk", - SortKey: "isk", - }, - }, - }, - } - rs := models.ResultSet{TableInfo: &tableDef} - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(&rs) - - testFS := testScriptFile(t, "test.tm", ` - table := session.current_table() - - assert(table.name == "test_table") - 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["hash"] == "ipk") - assert(table.gsis[0].keys["range"] == "isk") - - assert(table == session.result_set().table) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should return nil if no current result set", func(t *testing.T) { - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(nil) - - testFS := testScriptFile(t, "test.tm", ` - table := session.current_table() - - assert(table == nil) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) -} - -func TestModSession_Query(t *testing.T) { - t.Run("should successfully return query result", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "2") - mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[0]['pk'].S = abc") - mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[1]['pk'].S = 1232") - mockedUIService.EXPECT().PrintMessage(mock.Anything, "res[1].attr('size(pk)') = 4") - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - ui.print(res.length) - ui.print("res[0]['pk'].S = ", res[0].attr("pk")) - ui.print("res[1]['pk'].S = ", res[1].attr("pk")) - ui.print("res[1].attr('size(pk)') = ", res[1].attr("size(pk)")) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should return error if query returns error", func(t *testing.T) { - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(nil, errors.New("bang")) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.Error(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should successfully specify table name", func(t *testing.T) { - rs := &models.ResultSet{} - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{ - TableName: "some-table", - }).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr", { - table: "some-table", - }) - assert(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should successfully specify table proxy", func(t *testing.T) { - rs := &models.ResultSet{} - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(&models.ResultSet{ - TableInfo: &models.TableInfo{ - Name: "some-resultset-table", - }, - }) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{ - TableName: "some-resultset-table", - }).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr", { - table: session.result_set().table, - }) - assert(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should set placeholder values", func(t *testing.T) { - rs := &models.ResultSet{} - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, ":name = $value", scriptmanager.QueryOptions{ - NamePlaceholders: map[string]string{ - "name": "hello", - "value": "world", - }, - ValuePlaceholders: map[string]types.AttributeValue{ - "name": &types.AttributeValueMemberS{Value: "hello"}, - "value": &types.AttributeValueMemberS{Value: "world"}, - }, - }).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query(":name = $value", { - args: { - name: "hello", - value: "world", - }, - }) - assert(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should support various placeholder value type", func(t *testing.T) { - rs := &models.ResultSet{} - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, ":name = $value", scriptmanager.QueryOptions{ - NamePlaceholders: map[string]string{ - "str": "hello", - }, - ValuePlaceholders: map[string]types.AttributeValue{ - "str": &types.AttributeValueMemberS{Value: "hello"}, - "int": &types.AttributeValueMemberN{Value: "123"}, - "float": &types.AttributeValueMemberN{Value: "3.14"}, - "bool": &types.AttributeValueMemberBOOL{Value: true}, - "nil": &types.AttributeValueMemberNULL{Value: true}, - }, - }).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query(":name = $value", { - args: { - "str": "hello", - "int": 123, - "float": 3.14, - "bool": true, - "nil": nil, - }, - }) - assert(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should return error when placeholder value type is unsupported", func(t *testing.T) { - mockedSessionService := mocks.NewSessionService(t) - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query(":name = $value", { - args: { - "bad": func() { }, - }, - }) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.Error(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) -} - -func TestModSession_SelectedItem(t *testing.T) { - t.Run("should return selected item from service implementation", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(rs) - mockedSessionService.EXPECT().SelectedItemIndex(mock.Anything).Return(1) - - testFS := testScriptFile(t, "test.tm", ` - selItem := session.selected_item() - - assert(selItem != nil, "selItem != nil") - assert(selItem.index == 1, "selItem.index") - assert(selItem.result_set == session.result_set(), "selItem.result_set") - assert(selItem.attr('pk') == '1232', "selItem.attr('pk')") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should return nil if selected item returns -1", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().ResultSet(mock.Anything).Return(rs) - mockedSessionService.EXPECT().SelectedItemIndex(mock.Anything).Return(-1) - - testFS := testScriptFile(t, "test.tm", ` - selItem := session.selected_item() - - assert(selItem == nil, "selItem != nil") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) -} - -func TestModSession_SetResultSet(t *testing.T) { - t.Run("should set the result set on the session", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - mockedSessionService.EXPECT().SetResultSet(mock.Anything, rs) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - session.set_result_set(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modui.go b/internal/dynamo-browse/services/scriptmanager/modui.go deleted file mode 100644 index d53b2e4..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modui.go +++ /dev/null @@ -1,58 +0,0 @@ -package scriptmanager - -import ( - "context" - "strings" - - "github.com/risor-io/risor/object" -) - -type uiModule struct { - uiService UIService -} - -func (um *uiModule) print(ctx context.Context, args ...object.Object) object.Object { - var msg strings.Builder - for _, arg := range args { - if arg == nil { - continue - } - - switch a := arg.(type) { - case *object.String: - msg.WriteString(a.Value()) - default: - msg.WriteString(a.Inspect()) - } - } - - um.uiService.PrintMessage(ctx, msg.String()) - return object.Nil -} - -func (um *uiModule) prompt(ctx context.Context, args ...object.Object) object.Object { - if err := require("ui.prompt", 1, args); err != nil { - return err - } - - msg, _ := object.AsString(args[0]) - respChan := um.uiService.Prompt(ctx, msg) - - select { - case resp, hasResp := <-respChan: - if hasResp { - return object.NewString(resp) - } else { - return object.Nil - } - case <-ctx.Done(): - return object.NewError(ctx.Err()) - } -} - -func (um *uiModule) register() *object.Module { - return object.NewBuiltinsModule("ui", map[string]object.Object{ - "print": object.NewBuiltin("print", um.print), - "prompt": object.NewBuiltin("prompt", um.prompt), - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/modui_test.go b/internal/dynamo-browse/services/scriptmanager/modui_test.go deleted file mode 100644 index 3a8b96d..0000000 --- a/internal/dynamo-browse/services/scriptmanager/modui_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager/mocks" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "testing" -) - -func TestModUI_Prompt(t *testing.T) { - t.Run("should successfully return prompt value", func(t *testing.T) { - testFS := testScriptFile(t, "test.tm", ` - ui.print("Hello, world") - var name = ui.prompt("What is your name? ") - ui.print("Hello, " + name) - `) - - promptChan := make(chan string) - go func() { - promptChan <- "T. Test" - }() - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, world") - mockedUIService.EXPECT().Prompt(mock.Anything, "What is your name? ").Return(promptChan) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, T. Test") - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(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) - close(promptChan) - - ctx := context.Background() - - 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)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - }) - - t.Run("should return error if context 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") - `) - - promptChan := make(chan string) - ctx, cancelFn := context.WithCancel(context.Background()) - defer cancelFn() - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, world") - mockedUIService.EXPECT().Prompt(mock.Anything, "What is your name? ").Run(func(ctx context.Context, msg string) { - cancelFn() - }).Return(promptChan) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.Error(t, err) - - mockedUIService.AssertNotCalled(t, "Prompt", "after") - mockedUIService.AssertExpectations(t) - }) -} diff --git a/internal/dynamo-browse/services/scriptmanager/opts.go b/internal/dynamo-browse/services/scriptmanager/opts.go deleted file mode 100644 index 39d961f..0000000 --- a/internal/dynamo-browse/services/scriptmanager/opts.go +++ /dev/null @@ -1,26 +0,0 @@ -package scriptmanager - -import ( - "context" - "github.com/risor-io/risor/limits" -) - -// scriptEnv is the runtime environment for a particular script execution -type scriptEnv struct { - filename string -} - -type scriptEnvKeyType struct{} - -var scriptEnvKey = scriptEnvKeyType{} - -func scriptEnvFromCtx(ctx context.Context) scriptEnv { - perms, _ := ctx.Value(scriptEnvKey).(scriptEnv) - return perms -} - -func ctxWithScriptEnv(ctx context.Context, perms scriptEnv) context.Context { - newCtx := context.WithValue(ctx, scriptEnvKey, perms) - newCtx = limits.WithLimits(newCtx, limits.New()) - return newCtx -} diff --git a/internal/dynamo-browse/services/scriptmanager/relitem.go b/internal/dynamo-browse/services/scriptmanager/relitem.go deleted file mode 100644 index 63d7629..0000000 --- a/internal/dynamo-browse/services/scriptmanager/relitem.go +++ /dev/null @@ -1,57 +0,0 @@ -package scriptmanager - -import ( - "context" - "log" - "path" - - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/relitems" -) - -type relatedItem struct { - label string - table string - query *queryexpr.QueryExpr - onSelect func() error -} - -type relatedItemBuilder struct { - table string - itemProduction func(ctx context.Context, rs *models.ResultSet, index int) ([]relatedItem, error) -} - -func (s *Service) RelatedItemOfItem(ctx context.Context, rs *models.ResultSet, index int) ([]relitems.RelatedItem, error) { - riModels := []relitems.RelatedItem{} - - for _, plugin := range s.plugins { - for _, rb := range plugin.relatedItems { - // TODO: should support matching - match, _ := tableMatchesGlob(rb.table, rs.TableInfo.Name) - log.Printf("RelatedItemOfItem: table = '%v', pattern = '%v', match = '%v'", rb.table, rs.TableInfo.Name, match) - if match { - relatedItems, err := rb.itemProduction(ctx, rs, index) - if err != nil { - // TODO: should probably return error if no rel items were found and an error was raised - return nil, err - } - - // TODO: make this nicer - for _, ri := range relatedItems { - riModels = append(riModels, relitems.RelatedItem{ - Name: ri.label, - Query: ri.query, - Table: ri.table, - OnSelect: ri.onSelect, - }) - } - } - } - } - return riModels, nil -} - -func tableMatchesGlob(tableName, pattern string) (bool, error) { - return path.Match(tableName, pattern) -} diff --git a/internal/dynamo-browse/services/scriptmanager/resultsetproxy.go b/internal/dynamo-browse/services/scriptmanager/resultsetproxy.go deleted file mode 100644 index 953a3e4..0000000 --- a/internal/dynamo-browse/services/scriptmanager/resultsetproxy.go +++ /dev/null @@ -1,337 +0,0 @@ -package scriptmanager - -import ( - "context" - "time" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/attrutils" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr" - "github.com/pkg/errors" - "github.com/risor-io/risor/object" - "github.com/risor-io/risor/op" -) - -type resultSetProxy struct { - resultSet *models.ResultSet -} - -func newResultSetProxy(rs *models.ResultSet) *resultSetProxy { - return &resultSetProxy{resultSet: rs} -} - -func (r *resultSetProxy) SetAttr(name string, value object.Object) error { - return errors.Errorf("attribute error: %v", name) -} - -func (r *resultSetProxy) RunOperation(opType op.BinaryOpType, right object.Object) object.Object { - return object.Errorf("op error: unsupported %v", opType) -} - -func (r *resultSetProxy) Cost() int { - return len(r.resultSet.Items()) -} - -func (r *resultSetProxy) Interface() interface{} { - return r.resultSet -} - -func (r *resultSetProxy) IsTruthy() bool { - return true -} - -func (r *resultSetProxy) Type() object.Type { - return "resultset" -} - -func (r *resultSetProxy) Inspect() string { - return "resultset" -} - -func (r *resultSetProxy) Equals(other object.Object) object.Object { - otherRS, isOtherRS := other.(*resultSetProxy) - if !isOtherRS { - return object.False - } - - return object.NewBool(r.resultSet == otherRS.resultSet) -} - -// GetItem implements the [key] operator for a container type. -func (r *resultSetProxy) GetItem(key object.Object) (object.Object, *object.Error) { - idx, err := object.AsInt(key) - if err != nil { - return nil, err - } - - realIdx := int(idx) - if realIdx < 0 { - realIdx = len(r.resultSet.Items()) + realIdx - } - - if realIdx < 0 || realIdx >= len(r.resultSet.Items()) { - return nil, object.NewError(errors.Errorf("index error: index out of range: %v", idx)) - } - - return newItemProxy(r, realIdx), nil -} - -// GetSlice implements the [start:stop] operator for a container type. -func (r *resultSetProxy) GetSlice(s object.Slice) (object.Object, *object.Error) { - return nil, object.NewError(errors.New("TODO")) -} - -// SetItem implements the [key] = value operator for a container type. -func (r *resultSetProxy) SetItem(key, value object.Object) *object.Error { - return object.NewError(errors.New("TODO")) -} - -// DelItem implements the del [key] operator for a container type. -func (r *resultSetProxy) DelItem(key object.Object) *object.Error { - return object.NewError(errors.New("TODO")) -} - -// Contains returns true if the given item is found in this container. -func (r *resultSetProxy) Contains(item object.Object) *object.Bool { - // TODO - return object.False -} - -// Len returns the number of items in this container. -func (r *resultSetProxy) Len() *object.Int { - return object.NewInt(int64(len(r.resultSet.Items()))) -} - -// Iter returns an iterator for this container. -func (r *resultSetProxy) Iter() object.Iterator { - // TODO - return nil -} - -func (r *resultSetProxy) GetAttr(name string) (object.Object, bool) { - switch name { - case "table": - return &tableProxy{table: r.resultSet.TableInfo}, true - case "length": - return object.NewInt(int64(len(r.resultSet.Items()))), true - case "find": - return object.NewBuiltin("find", r.find), true - case "merge": - return object.NewBuiltin("merge", r.merge), true - } - - return nil, false -} - -func (i *resultSetProxy) find(ctx context.Context, args ...object.Object) object.Object { - if objErr := require("resultset.find", 1, args); objErr != nil { - return objErr - } - - str, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - modExpr, err := queryexpr.Parse(str) - if err != nil { - return object.Errorf("arg error: invalid path expression: %v", err) - } - - for idx, item := range i.resultSet.Items() { - rs, err := modExpr.EvalItem(item) - if err != nil { - continue - } - - if attrutils.Truthy(rs) { - return newItemProxy(i, idx) - } - } - - return object.Nil -} - -func (i *resultSetProxy) merge(ctx context.Context, args ...object.Object) object.Object { - type pksk struct { - pk types.AttributeValue - sk types.AttributeValue - } - - if objErr := require("resultset.merge", 1, args); objErr != nil { - return objErr - } - - otherRS, isRS := args[0].(*resultSetProxy) - if !isRS { - return object.NewError(errors.Errorf("type error: expected a resultset (got %v)", args[0].Type())) - } - - if !i.resultSet.TableInfo.Equal(otherRS.resultSet.TableInfo) { - return object.Nil - } - - itemsInI := make(map[pksk]models.Item) - newItems := make([]models.Item, 0, len(i.resultSet.Items())+len(otherRS.resultSet.Items())) - for _, item := range i.resultSet.Items() { - pk, sk := item.PKSK(i.resultSet.TableInfo) - itemsInI[pksk{pk, sk}] = item - newItems = append(newItems, item) - } - - for _, item := range otherRS.resultSet.Items() { - pk, sk := item.PKSK(i.resultSet.TableInfo) - if _, hasItem := itemsInI[pksk{pk, sk}]; !hasItem { - newItems = append(newItems, item) - } - } - - newResultSet := &models.ResultSet{ - Created: time.Now(), - TableInfo: i.resultSet.TableInfo, - } - newResultSet.SetItems(newItems) - - return &resultSetProxy{resultSet: newResultSet} -} - -type itemProxy struct { - resultSetProxy *resultSetProxy - itemIndex int - item models.Item -} - -func (i *itemProxy) SetAttr(name string, value object.Object) error { - return errors.Errorf("attribute error: %v", name) -} - -func (i *itemProxy) RunOperation(opType op.BinaryOpType, right object.Object) object.Object { - return object.Errorf("op error: unsupported %v", opType) -} - -func (i *itemProxy) Cost() int { - return len(i.item) -} - -func newItemProxy(rs *resultSetProxy, itemIndex int) *itemProxy { - return &itemProxy{ - resultSetProxy: rs, - itemIndex: itemIndex, - item: rs.resultSet.Items()[itemIndex], - } -} - -func (i *itemProxy) Interface() interface{} { - return i.item -} - -func (i *itemProxy) IsTruthy() bool { - return true -} - -func (i *itemProxy) Type() object.Type { - return "item" -} - -func (i *itemProxy) Inspect() string { - return "item" -} - -func (i *itemProxy) Equals(other object.Object) object.Object { - // TODO - return object.False -} - -func (i *itemProxy) GetAttr(name string) (object.Object, bool) { - // TODO: this should implement the container interface - switch name { - case "result_set": - return i.resultSetProxy, true - case "index": - return object.NewInt(int64(i.itemIndex)), true - case "attr": - return object.NewBuiltin("attr", i.value), true - case "set_attr": - return object.NewBuiltin("set_attr", i.setValue), true - case "delete_attr": - return object.NewBuiltin("delete_attr", i.deleteAttr), true - } - - return nil, false -} - -func (i *itemProxy) value(ctx context.Context, args ...object.Object) object.Object { - if objErr := require("item.attr", 1, args); objErr != nil { - return objErr - } - - str, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - modExpr, err := queryexpr.Parse(str) - if err != nil { - return object.Errorf("arg error: invalid path expression: %v", err) - } - av, err := modExpr.EvalItem(i.item) - if err != nil { - return object.NewError(errors.Errorf("arg error: path expression evaluate error: %v", err)) - } - - tVal, err := attributeValueToTamarin(av) - if err != nil { - return object.NewError(err) - } - return tVal -} - -func (i *itemProxy) setValue(ctx context.Context, args ...object.Object) object.Object { - if objErr := require("item.set_attr", 2, args); objErr != nil { - return objErr - } - - pathExpr, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - path, err := queryexpr.Parse(pathExpr) - if err != nil { - return object.Errorf("arg error: invalid path expression: %v", err) - } - - newValue, err := tamarinValueToAttributeValue(args[1]) - if err != nil { - return object.NewError(err) - } - if err := path.SetEvalItem(i.item, newValue); err != nil { - return object.NewError(err) - } - - i.resultSetProxy.resultSet.SetDirty(i.itemIndex, true) - return nil -} - -func (i *itemProxy) deleteAttr(ctx context.Context, args ...object.Object) object.Object { - if objErr := require("item.delete_attr", 1, args); objErr != nil { - return objErr - } - - str, objErr := object.AsString(args[0]) - if objErr != nil { - return objErr - } - - modExpr, err := queryexpr.Parse(str) - if err != nil { - return object.Errorf("arg error: invalid path expression: %v", err) - } - if err := modExpr.DeleteAttribute(i.item); err != nil { - return object.NewError(errors.Errorf("arg error: path expression evaluate error: %v", err)) - } - - i.resultSetProxy.resultSet.SetDirty(i.itemIndex, true) - return nil -} diff --git a/internal/dynamo-browse/services/scriptmanager/resultsetproxy_test.go b/internal/dynamo-browse/services/scriptmanager/resultsetproxy_test.go deleted file mode 100644 index 3b7f354..0000000 --- a/internal/dynamo-browse/services/scriptmanager/resultsetproxy_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager/mocks" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestResultSetProxy(t *testing.T) { - t.Run("should property return properties of a resultset and item", func(t *testing.T) { - rs := &models.ResultSet{ - TableInfo: &models.TableInfo{ - Name: "test-table", - }, - } - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - - // Test properties of the result set - assert(res.table.name, "hello") - - assert(res == res, "result_set.equals") - assert(res.length == 2, "result_set.length") - - // Test properties of items - assert(res[0].index == 0, "res[0].index") - assert(res[0].result_set == res, "res[0].result_set") - assert(res[0].attr('pk') == 'abc', "res[0].attr('pk')") - - assert(res[1].attr('pk') == '1232', "res[1].attr('pk')") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) -} - -func TestResultSetProxy_Find(t *testing.T) { - t.Run("should return the first item that matches the given expression", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "abc"}, "sk": &types.AttributeValueMemberS{Value: "abc"}, "primary": &types.AttributeValueMemberS{Value: "yes"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}, "findMe": &types.AttributeValueMemberS{Value: "yes"}}, - {"pk": &types.AttributeValueMemberS{Value: "2345"}, "findMe": &types.AttributeValueMemberS{Value: "second"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - - assert(res.find('findMe is "any"').attr("pk") == "1232") - assert(res.find('findMe = "second"').attr("pk") == "2345") - assert(res.find('pk = sk').attr("primary") == "yes") - - assert(res.find('findMe = "missing"') == nil) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) -} - -func TestResultSetProxy_Merge(t *testing.T) { - t.Run("should return a result set with items from both if both are from the same table", func(t *testing.T) { - td := &models.TableInfo{Name: "test", Keys: models.KeyAttribute{PartitionKey: "pk", SortKey: "sk"}} - - rs1 := &models.ResultSet{TableInfo: td} - rs1.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}, "sk": &types.AttributeValueMemberS{Value: "123"}}, - }) - - rs2 := &models.ResultSet{TableInfo: td} - rs2.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "bcd"}, "sk": &types.AttributeValueMemberS{Value: "234"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "rs1", scriptmanager.QueryOptions{}).Return(rs1, nil) - mockedSessionService.EXPECT().Query(mock.Anything, "rs2", scriptmanager.QueryOptions{}).Return(rs2, nil) - - testFS := testScriptFile(t, "test.tm", ` - r1 := session.query("rs1") - r2 := session.query("rs2") - - res := r1.merge(r2) - - assert(res[0].attr("pk") == "abc") - assert(res[0].attr("sk") == "123") - assert(res[1].attr("pk") == "bcd") - assert(res[1].attr("sk") == "234") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) - - t.Run("should return nil if result-sets are from different tables", func(t *testing.T) { - td1 := &models.TableInfo{Name: "test", Keys: models.KeyAttribute{PartitionKey: "pk", SortKey: "sk"}} - rs1 := &models.ResultSet{TableInfo: td1} - rs1.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}, "sk": &types.AttributeValueMemberS{Value: "123"}}, - }) - - td2 := &models.TableInfo{Name: "test2", Keys: models.KeyAttribute{PartitionKey: "pk2", SortKey: "sk"}} - rs2 := &models.ResultSet{TableInfo: td2} - rs2.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "bcd"}, "sk": &types.AttributeValueMemberS{Value: "234"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "rs1", scriptmanager.QueryOptions{}).Return(rs1, nil) - mockedSessionService.EXPECT().Query(mock.Anything, "rs2", scriptmanager.QueryOptions{}).Return(rs2, nil) - - testFS := testScriptFile(t, "test.tm", ` - r1 := session.query("rs1") - r2 := session.query("rs2") - - res := r1.merge(r2) - - assert(res == nil) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) -} - -func TestResultSetProxy_GetAttr(t *testing.T) { - t.Run("should return the value of items within a result set", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - { - "pk": &types.AttributeValueMemberS{Value: "abc"}, - "sk": &types.AttributeValueMemberN{Value: "123"}, - "bool": &types.AttributeValueMemberBOOL{Value: true}, - "null": &types.AttributeValueMemberNULL{Value: true}, - "list": &types.AttributeValueMemberL{Value: []types.AttributeValue{ - &types.AttributeValueMemberS{Value: "apple"}, - &types.AttributeValueMemberS{Value: "banana"}, - &types.AttributeValueMemberS{Value: "cherry"}, - }}, - "map": &types.AttributeValueMemberM{Value: map[string]types.AttributeValue{ - "this": &types.AttributeValueMemberS{Value: "that"}, - "another": &types.AttributeValueMemberS{Value: "thing"}, - }}, - "strSet": &types.AttributeValueMemberSS{Value: []string{"apple", "banana", "cherry"}}, - "numSet": &types.AttributeValueMemberNS{Value: []string{"123", "45.67", "8.911", "-321"}}, - }, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - - assert(res[0].attr("pk") == "abc", "str attr") - assert(res[0].attr("sk") == 123, "num attr") - assert(res[0].attr("bool") == true, "bool attr") - assert(res[0].attr("null") == nil, "null attr") - assert(res[0].attr("list") == ["apple","banana","cherry"], "list attr") - assert(res[0].attr("map") == {"this":"that", "another":"thing"}, "map attr") - assert(res[0].attr("strSet") == {"apple","banana","cherry"}, "string set") - assert(res[0].attr("numSet") == {123, 45.67, 8.911, -321}, "number set") - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedSessionService.AssertExpectations(t) - }) -} - -func TestResultSetProxy_SetAttr(t *testing.T) { - t.Run("should set the value of the item within a result set", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - mockedSessionService.EXPECT().SetResultSet(mock.Anything, mock.MatchedBy(func(rs *models.ResultSet) bool { - assert.Equal(t, "bla-di-bla", rs.Items()[0]["pk"].(*types.AttributeValueMemberS).Value) - assert.Equal(t, "123", rs.Items()[0]["num"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "123.45", rs.Items()[0]["numFloat"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, true, rs.Items()[0]["bool"].(*types.AttributeValueMemberBOOL).Value) - assert.Equal(t, true, rs.Items()[0]["nil"].(*types.AttributeValueMemberNULL).Value) - - list := rs.Items()[0]["lists"].(*types.AttributeValueMemberL).Value - assert.Equal(t, "abc", list[0].(*types.AttributeValueMemberS).Value) - assert.Equal(t, "123", list[1].(*types.AttributeValueMemberN).Value) - assert.Equal(t, true, list[2].(*types.AttributeValueMemberBOOL).Value) - - nestedLists := rs.Items()[0]["nestedLists"].(*types.AttributeValueMemberL).Value - assert.Equal(t, "1", nestedLists[0].(*types.AttributeValueMemberL).Value[0].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "2", nestedLists[0].(*types.AttributeValueMemberL).Value[1].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "3", nestedLists[1].(*types.AttributeValueMemberL).Value[0].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "4", nestedLists[1].(*types.AttributeValueMemberL).Value[1].(*types.AttributeValueMemberN).Value) - - mapValue := rs.Items()[0]["map"].(*types.AttributeValueMemberM).Value - assert.Equal(t, "world", mapValue["hello"].(*types.AttributeValueMemberS).Value) - assert.Equal(t, "213", mapValue["nums"].(*types.AttributeValueMemberN).Value) - - numSet := rs.Items()[0]["numSet"].(*types.AttributeValueMemberNS).Value - assert.Len(t, numSet, 4) - assert.Contains(t, numSet, "1") - assert.Contains(t, numSet, "2") - assert.Contains(t, numSet, "3") - assert.Contains(t, numSet, "4.5") - - strSet := rs.Items()[0]["strSet"].(*types.AttributeValueMemberSS).Value - assert.Len(t, strSet, 3) - assert.Contains(t, strSet, "a") - assert.Contains(t, strSet, "b") - assert.Contains(t, strSet, "c") - - assert.True(t, rs.IsDirty(0)) - return true - })) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - - res[0].set_attr("pk", "bla-di-bla") - res[0].set_attr("num", 123) - res[0].set_attr("numFloat", 123.45) - res[0].set_attr("bool", true) - res[0].set_attr("nil", nil) - res[0].set_attr("lists", ['abc', 123, true]) - res[0].set_attr("nestedLists", [[1,2], [3,4]]) - res[0].set_attr("map", {"hello": "world", "nums": 213}) - res[0].set_attr("numSet", {1,2,3,4.5}) - res[0].set_attr("strSet", {"a","b","c"}) - - session.set_result_set(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) -} - -func TestResultSetProxy_DeleteAttr(t *testing.T) { - t.Run("should delete the value of the item within a result set", func(t *testing.T) { - rs := &models.ResultSet{} - rs.SetItems([]models.Item{ - {"pk": &types.AttributeValueMemberS{Value: "abc"}, "deleteMe": &types.AttributeValueMemberBOOL{Value: true}}, - {"pk": &types.AttributeValueMemberS{Value: "1232"}}, - }) - - mockedSessionService := mocks.NewSessionService(t) - mockedSessionService.EXPECT().Query(mock.Anything, "some expr", scriptmanager.QueryOptions{}).Return(rs, nil) - mockedSessionService.EXPECT().SetResultSet(mock.Anything, mock.MatchedBy(func(rs *models.ResultSet) bool { - assert.Equal(t, "abc", rs.Items()[0]["pk"].(*types.AttributeValueMemberS).Value) - assert.Nil(t, rs.Items()[0]["deleteMe"]) - assert.True(t, rs.IsDirty(0)) - return true - })) - - mockedUIService := mocks.NewUIService(t) - - testFS := testScriptFile(t, "test.tm", ` - res := session.query("some expr") - res[0].delete_attr("deleteMe") - session.set_result_set(res) - `) - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - Session: mockedSessionService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - mockedSessionService.AssertExpectations(t) - }) - -} diff --git a/internal/dynamo-browse/services/scriptmanager/scrsched.go b/internal/dynamo-browse/services/scriptmanager/scrsched.go deleted file mode 100644 index 3846676..0000000 --- a/internal/dynamo-browse/services/scriptmanager/scrsched.go +++ /dev/null @@ -1,53 +0,0 @@ -package scriptmanager - -import ( - "context" - "github.com/pkg/errors" - "time" -) - -type scriptScheduler struct { - jobChan chan scriptJob -} - -func newScriptScheduler() *scriptScheduler { - ss := &scriptScheduler{} - ss.start() - return ss -} - -func (ss *scriptScheduler) start() { - ss.jobChan = make(chan scriptJob) - go func() { - for job := range ss.jobChan { - job.job(job.ctx) - } - }() -} - -// startJobOnceFree will submit a script execution job. The function will wait until the scheduler is free. -// The job will then run on the script goroutine and the function will return. -func (ss *scriptScheduler) startJobOnceFree(ctx context.Context, job func(ctx context.Context)) error { - select { - case ss.jobChan <- scriptJob{ctx: ctx, job: job}: - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -// runNow will submit a job for immediate execution. The job will run as long as the scheduler is free. -// If the scheduler is not free, an error will be returned and the job will not run. -func (ss *scriptScheduler) runNow(ctx context.Context, job func(ctx context.Context)) error { - select { - case ss.jobChan <- scriptJob{ctx: ctx, job: job}: - return nil - case <-time.After(500 * time.Millisecond): - return errors.New("a script is already running") - } -} - -type scriptJob struct { - ctx context.Context - job func(ctx context.Context) -} diff --git a/internal/dynamo-browse/services/scriptmanager/service.go b/internal/dynamo-browse/services/scriptmanager/service.go deleted file mode 100644 index eac6638..0000000 --- a/internal/dynamo-browse/services/scriptmanager/service.go +++ /dev/null @@ -1,250 +0,0 @@ -package scriptmanager - -import ( - "context" - "io/fs" - "log" - "os" - "path/filepath" - "strings" - - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/keybindings" - "github.com/pkg/errors" - "github.com/risor-io/risor" - "github.com/risor-io/risor/object" -) - -var ( - relPrefix = "." + string(filepath.Separator) -) - -type Service struct { - lookupPaths []fs.FS - ifaces Ifaces - sched *scriptScheduler - plugins []*ScriptPlugin -} - -func New(opts ...ServiceOption) *Service { - srv := &Service{ - lookupPaths: nil, - sched: newScriptScheduler(), - } - for _, opt := range opts { - opt(srv) - } - return srv -} - -func (s *Service) SetLookupPaths(fs []fs.FS) { - s.lookupPaths = fs -} - -func (s *Service) SetIFaces(ifaces Ifaces) { - s.ifaces = ifaces -} - -func (s *Service) LoadScript(ctx context.Context, filename string) (*ScriptPlugin, error) { - resChan := make(chan loadedScriptResult) - - if err := s.sched.startJobOnceFree(ctx, func(ctx context.Context) { - s.loadScript(ctx, filename, resChan) - }); err != nil { - return nil, err - } - - res := <-resChan - if res.err != nil { - return nil, res.err - } - - // Look for the previous version. If one is there, replace it, otherwise add it - // TODO: this should probably be protected by a mutex - newPlugin := res.scriptPlugin - for i, p := range s.plugins { - if p.name == newPlugin.name { - s.plugins[i] = newPlugin - return newPlugin, nil - } - } - - s.plugins = append(s.plugins, newPlugin) - return newPlugin, nil -} - -func (s *Service) RunAdHocScript(ctx context.Context, filename string) chan error { - errChan := make(chan error) - go s.startAdHocScript(ctx, filename, errChan) - return errChan -} - -func (s *Service) StartAdHocScript(ctx context.Context, filename string, errChan chan error) error { - return s.sched.startJobOnceFree(ctx, func(ctx context.Context) { - s.startAdHocScript(ctx, filename, errChan) - }) -} - -func (s *Service) startAdHocScript(ctx context.Context, filename string, errChan chan error) { - defer close(errChan) - - code, err := s.readScript(filename, true) - if err != nil { - errChan <- errors.Wrapf(err, "cannot load script file %v", filename) - return - } - - ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename)}) - - if _, err := risor.Eval(ctx, code, - risor.WithGlobals(s.builtins()), - ); err != nil { - errChan <- errors.Wrapf(err, "script %v", filename) - return - } -} - -type loadedScriptResult struct { - scriptPlugin *ScriptPlugin - err error -} - -func (s *Service) loadScript(ctx context.Context, filename string, resChan chan loadedScriptResult) { - defer close(resChan) - - code, err := s.readScript(filename, false) - if err != nil { - resChan <- loadedScriptResult{err: errors.Wrapf(err, "cannot load script file %v", filename)} - return - } - - newPlugin := &ScriptPlugin{ - name: strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)), - scriptService: s, - } - - ctx = ctxWithScriptEnv(ctx, scriptEnv{filename: filepath.Base(filename)}) - - if _, err := risor.Eval(ctx, code, - risor.WithGlobals(s.builtins()), - risor.WithGlobals(map[string]any{ - "ext": (&extModule{scriptPlugin: newPlugin}).register(), - }), - ); err != nil { - resChan <- loadedScriptResult{err: errors.Wrapf(err, "script %v", filename)} - return - } - - resChan <- loadedScriptResult{scriptPlugin: newPlugin} -} - -func (s *Service) readScript(filename string, allowCwd bool) (string, 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 "", err - } - return string(code), nil - } - } else { - log.Printf("warn: cannot get cwd for reading script %v: %v", filename, err) - } - } - - if strings.HasPrefix(filename, string(filepath.Separator)) || strings.HasPrefix(filename, relPrefix) { - code, err := os.ReadFile(filename) - if err != nil { - return "", err - } - return string(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) { - continue - } else { - return "", err - } - } else if stat.IsDir() { - continue - } - - code, err := fs.ReadFile(currFS, filename) - if err == nil { - return string(code), nil - } else { - return "", err - } - } - - return "", os.ErrNotExist -} - -// LookupCommand looks up a command defined by a script. -// TODO: Command should probably accept/return a chan error to indicate that this will run in a separate goroutine -func (s *Service) LookupCommand(name string) *Command { - for _, p := range s.plugins { - if cmd, hasCmd := p.definedCommands[name]; hasCmd { - return cmd - } - } - return nil -} - -func (s *Service) LookupKeyBinding(key string) (string, *Command) { - for _, p := range s.plugins { - if bindingName, hasBinding := p.keyToKeyBinding[key]; hasBinding { - if cmd, hasCmd := p.definedKeyBindings[bindingName]; hasCmd { - return bindingName, cmd - } - } - } - return "", nil -} - -func (s *Service) UnbindKey(key string) { - for _, p := range s.plugins { - if _, hasBinding := p.keyToKeyBinding[key]; hasBinding { - delete(p.keyToKeyBinding, key) - } - } -} - -func (s *Service) RebindKeyBinding(keyBinding string, newKey string) error { - if newKey == "" { - for _, p := range s.plugins { - for k, b := range p.keyToKeyBinding { - if b == keyBinding { - delete(p.keyToKeyBinding, k) - } - } - } - return nil - } - - for _, p := range s.plugins { - if _, hasCmd := p.definedKeyBindings[keyBinding]; hasCmd { - if newKey != "" { - p.keyToKeyBinding[newKey] = keyBinding - } - return nil - } - } - - return keybindings.InvalidBindingError(keyBinding) -} - -func (s *Service) builtins() map[string]any { - return map[string]any{ - "ui": (&uiModule{uiService: s.ifaces.UI}).register(), - "session": (&sessionModule{sessionService: s.ifaces.Session}).register(), - "print": object.NewBuiltin("print", printBuiltin), - "printf": object.NewBuiltin("printf", printfBuiltin), - } -} diff --git a/internal/dynamo-browse/services/scriptmanager/service_test.go b/internal/dynamo-browse/services/scriptmanager/service_test.go deleted file mode 100644 index 745124d..0000000 --- a/internal/dynamo-browse/services/scriptmanager/service_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package scriptmanager_test - -import ( - "context" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/scriptmanager/mocks" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "io/fs" - "testing" - "testing/fstest" - "time" -) - -func TestService_RunAdHocScript(t *testing.T) { - t.Run("successfully loads and executes a script", func(t *testing.T) { - testFS := testScriptFile(t, "test.tm", ` - ui.print("Hello, world") - `) - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, world") - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - ctx := context.Background() - err := <-srv.RunAdHocScript(ctx, "test.tm") - assert.NoError(t, err) - - mockedUIService.AssertExpectations(t) - }) -} - -func TestService_LoadScript(t *testing.T) { - t.Run("successfully loads a script and exposes it as a plugin", func(t *testing.T) { - testFS := testScriptFile(t, "test.tm", ` - ext.command("somewhere", func(a) { - ui.print("Hello, " + a) - }) - `) - - ctx := context.Background() - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, someone") - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - plugin, err := srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - assert.NotNil(t, plugin) - assert.Equal(t, "test", plugin.Name()) - - cmd := srv.LookupCommand("somewhere") - assert.NotNil(t, cmd) - - errChan := make(chan error) - err = cmd.Invoke(ctx, []string{"someone"}, errChan) - assert.NoError(t, err) - assert.NoError(t, waitForErr(t, errChan)) - - mockedUIService.AssertExpectations(t) - }) - - t.Run("reloading a script with the same name should remove the old one", func(t *testing.T) { - testFS := fstest.MapFS{ - "test.tm": &fstest.MapFile{ - Data: []byte(` - ext.command("somewhere", func(a) { - ui.print("Hello, " + a) - }) - `), - }, - } - - ctx := context.Background() - - mockedUIService := mocks.NewUIService(t) - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Hello, someone").Once() - mockedUIService.EXPECT().PrintMessage(mock.Anything, "Goodbye, someone").Once() - - srv := scriptmanager.New(scriptmanager.WithFS(testFS)) - srv.SetIFaces(scriptmanager.Ifaces{ - UI: mockedUIService, - }) - - // Execute the old script - _, err := srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - - cmd := srv.LookupCommand("somewhere") - assert.NotNil(t, cmd) - - errChan := make(chan error) - err = cmd.Invoke(ctx, []string{"someone"}, errChan) - assert.NoError(t, err) - assert.NoError(t, waitForErr(t, errChan)) - - // Change the script and reload - testFS["test.tm"] = &fstest.MapFile{ - Data: []byte(` - ext.command("somewhere", func(a) { - ui.print("Goodbye, " + a) - }) - `), - } - - _, err = srv.LoadScript(ctx, "test.tm") - assert.NoError(t, err) - - cmd = srv.LookupCommand("somewhere") - assert.NotNil(t, cmd) - - errChan = make(chan error) - err = cmd.Invoke(ctx, []string{"someone"}, errChan) - assert.NoError(t, err) - assert.NoError(t, waitForErr(t, errChan)) - - mockedUIService.AssertExpectations(t) - }) -} - -func testScriptFile(t *testing.T, filename, code string) fs.FS { - t.Helper() - - testFs := fstest.MapFS{ - filename: &fstest.MapFile{ - Data: []byte(code), - }, - } - return testFs -} - -func waitForErr(t *testing.T, errChan chan error) error { - t.Helper() - - select { - case err := <-errChan: - return err - case <-time.After(5 * time.Second): - t.Fatalf("timed-out waiting for an error") - } - return nil -} diff --git a/internal/dynamo-browse/services/scriptmanager/serviceopts.go b/internal/dynamo-browse/services/scriptmanager/serviceopts.go deleted file mode 100644 index 6841531..0000000 --- a/internal/dynamo-browse/services/scriptmanager/serviceopts.go +++ /dev/null @@ -1,11 +0,0 @@ -package scriptmanager - -import "io/fs" - -type ServiceOption func(srv *Service) - -func WithFS(fs ...fs.FS) ServiceOption { - return func(srv *Service) { - srv.lookupPaths = fs - } -} diff --git a/internal/dynamo-browse/services/scriptmanager/tableproxy.go b/internal/dynamo-browse/services/scriptmanager/tableproxy.go deleted file mode 100644 index 252348f..0000000 --- a/internal/dynamo-browse/services/scriptmanager/tableproxy.go +++ /dev/null @@ -1,138 +0,0 @@ -package scriptmanager - -import ( - "github.com/lmika/dynamo-browse/internal/common/sliceutils" - "github.com/lmika/dynamo-browse/internal/dynamo-browse/models" - "github.com/pkg/errors" - "github.com/risor-io/risor/object" - "github.com/risor-io/risor/op" - "reflect" -) - -const ( - tableProxyPartitionKey = "hash" - tableProxySortKey = "range" -) - -type tableProxy struct { - table *models.TableInfo -} - -func (t *tableProxy) SetAttr(name string, value object.Object) error { - return errors.Errorf("attribute error: %v", name) -} - -func (t *tableProxy) RunOperation(opType op.BinaryOpType, right object.Object) object.Object { - return object.Errorf("op error: unsupported %v", opType) -} - -func (t *tableProxy) Cost() int { - return 0 -} - -func (t *tableProxy) Type() object.Type { - return "table" -} - -func (t *tableProxy) Inspect() string { - return "table(" + t.table.Name + ")" -} - -func (t *tableProxy) Interface() interface{} { - return t.table -} - -func (t *tableProxy) Equals(other object.Object) object.Object { - otherT, isOtherRS := other.(*tableProxy) - if !isOtherRS { - return object.False - } - - return object.NewBool(reflect.DeepEqual(t.table, otherT.table)) -} - -func (t *tableProxy) GetAttr(name string) (object.Object, bool) { - switch name { - 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), - }), true - case "gsis": - return object.NewList(sliceutils.Map(t.table.GSIs, newTableIndexProxy)), true - } - - return nil, false -} - -func (t *tableProxy) IsTruthy() bool { - return true -} - -type tableIndexProxy struct { - gsi models.TableGSI -} - -func (t tableIndexProxy) SetAttr(name string, value object.Object) error { - return errors.Errorf("attribute error: %v", name) -} - -func (t tableIndexProxy) RunOperation(opType op.BinaryOpType, right object.Object) object.Object { - return object.Errorf("op error: unsupported %v", opType) -} - -func (t tableIndexProxy) Cost() int { - return 0 -} - -func newTableIndexProxy(gsi models.TableGSI) object.Object { - return tableIndexProxy{gsi: gsi} -} - -func (t tableIndexProxy) Type() object.Type { - return "table_index" -} - -func (t tableIndexProxy) Inspect() string { - return "table_index(gsi," + t.gsi.Name + ")" -} - -func (t tableIndexProxy) Interface() interface{} { - return t.gsi -} - -func (t tableIndexProxy) Equals(other object.Object) object.Object { - otherIP, isOtherIP := other.(tableIndexProxy) - if !isOtherIP { - return object.False - } - - return object.NewBool(reflect.DeepEqual(t.gsi, otherIP.gsi)) -} - -func (t tableIndexProxy) GetAttr(name string) (object.Object, bool) { - switch name { - case "name": - return object.NewString(t.gsi.Name), true - case "keys": - return object.NewMap(map[string]object.Object{ - tableProxyPartitionKey: object.NewString(t.gsi.Keys.PartitionKey), - tableProxySortKey: object.NewString(t.gsi.Keys.SortKey), - }), true - } - - return nil, false -} - -func (t tableIndexProxy) IsTruthy() bool { - return true -} diff --git a/internal/dynamo-browse/services/scriptmanager/typemapping.go b/internal/dynamo-browse/services/scriptmanager/typemapping.go deleted file mode 100644 index d7b8a47..0000000 --- a/internal/dynamo-browse/services/scriptmanager/typemapping.go +++ /dev/null @@ -1,135 +0,0 @@ -package scriptmanager - -import ( - "fmt" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/lmika/dynamo-browse/internal/common/maputils" - "github.com/lmika/dynamo-browse/internal/common/sliceutils" - "github.com/pkg/errors" - "github.com/risor-io/risor/object" - "regexp" - "strconv" -) - -func tamarinValueToAttributeValue(val object.Object) (types.AttributeValue, error) { - switch v := val.(type) { - case *object.String: - return &types.AttributeValueMemberS{Value: v.Value()}, nil - case *object.Int: - return &types.AttributeValueMemberN{Value: strconv.FormatInt(v.Value(), 10)}, nil - case *object.Float: - return &types.AttributeValueMemberN{Value: strconv.FormatFloat(v.Value(), 'f', -1, 64)}, nil - case *object.Bool: - return &types.AttributeValueMemberBOOL{Value: v.Value()}, nil - case *object.NilType: - return &types.AttributeValueMemberNULL{Value: true}, nil - case *object.List: - attrValue, err := sliceutils.MapWithError(v.Value(), tamarinValueToAttributeValue) - if err != nil { - return nil, err - } - return &types.AttributeValueMemberL{Value: attrValue}, nil - case *object.Map: - attrValue, err := maputils.MapValuesWithError(v.Value(), tamarinValueToAttributeValue) - if err != nil { - return nil, err - } - return &types.AttributeValueMemberM{Value: attrValue}, nil - case *object.Set: - values := maputils.Values(v.Value()) - canBeNumSet := sliceutils.All(values, func(t object.Object) bool { - _, isInt := t.(*object.Int) - _, isFloat := t.(*object.Float) - return isInt || isFloat - }) - - if canBeNumSet { - return &types.AttributeValueMemberNS{ - Value: sliceutils.Map(values, func(t object.Object) string { - switch v := t.(type) { - case *object.Int: - return strconv.FormatInt(v.Value(), 10) - case *object.Float: - return strconv.FormatFloat(v.Value(), 'f', -1, 64) - } - panic(fmt.Sprintf("unhandled object type: %v", t.Type())) - }), - }, nil - } - return &types.AttributeValueMemberSS{ - Value: sliceutils.Map(values, func(t object.Object) string { - v, _ := object.AsString(t) - return v - }), - }, nil - } - return nil, errors.Errorf("type error: unsupported value type (got %v)", val.Type()) -} - -func attributeValueToTamarin(val types.AttributeValue) (object.Object, error) { - if val == nil { - return object.Nil, nil - } - - switch v := val.(type) { - case *types.AttributeValueMemberS: - return object.NewString(v.Value), nil - case *types.AttributeValueMemberN: - f, err := convertNumAttributeToTamarinValue(v.Value) - if err != nil { - return nil, errors.Errorf("value error: invalid N value: %v", v.Value) - } - return f, nil - case *types.AttributeValueMemberBOOL: - if v.Value { - return object.True, nil - } - return object.False, nil - case *types.AttributeValueMemberNULL: - return object.Nil, nil - case *types.AttributeValueMemberL: - list, err := sliceutils.MapWithError(v.Value, attributeValueToTamarin) - if err != nil { - return nil, err - } - return object.NewList(list), nil - case *types.AttributeValueMemberM: - objMap, err := maputils.MapValuesWithError(v.Value, attributeValueToTamarin) - if err != nil { - return nil, err - } - return object.NewMap(objMap), nil - case *types.AttributeValueMemberSS: - return object.NewSet(sliceutils.Map(v.Value, func(s string) object.Object { - return object.NewString(s) - })), nil - case *types.AttributeValueMemberNS: - nums, err := sliceutils.MapWithError(v.Value, func(s string) (object.Object, error) { - return convertNumAttributeToTamarinValue(s) - }) - if err != nil { - return nil, err - } - return object.NewSet(nums), nil - } - return nil, errors.Errorf("value error: cannot convert type %T to tamarin object", val) -} - -var intNumberPattern = regexp.MustCompile(`^[-]?[0-9]+$`) - -// XXX - this is pretty crappy in that it does not support large values -func convertNumAttributeToTamarinValue(n string) (object.Object, error) { - if intNumberPattern.MatchString(n) { - parsedInt, err := strconv.ParseInt(n, 10, 64) - if err != nil { - return nil, err - } - return object.NewInt(parsedInt), nil - } - - f, err := strconv.ParseFloat(n, 64) - if err != nil { - return nil, err - } - return object.NewFloat(f), nil -} diff --git a/internal/dynamo-browse/services/scriptmanager/types.go b/internal/dynamo-browse/services/scriptmanager/types.go deleted file mode 100644 index 442f03b..0000000 --- a/internal/dynamo-browse/services/scriptmanager/types.go +++ /dev/null @@ -1,35 +0,0 @@ -package scriptmanager - -import ( - "context" -) - -type ScriptPlugin struct { - scriptService *Service - name string - definedCommands map[string]*Command - definedKeyBindings map[string]*Command - keyToKeyBinding map[string]string - relatedItems []*relatedItemBuilder -} - -func (sp *ScriptPlugin) Name() string { - return sp.name -} - -type Command struct { - plugin *ScriptPlugin - cmdFn func(ctx context.Context, args []string) error -} - -// Invoke will schedule the command for invocation. If the script scheduler is free, it will be started immediately. -// Otherwise an error will be returned. -func (c *Command) Invoke(ctx context.Context, args []string, errChan chan error) error { - return c.plugin.scriptService.sched.runNow(ctx, func(ctx context.Context) { - errChan <- c.cmdFn(ctx, args) - }) -} - -//func (c *Command) LookupRelevantItems(ctx context.Context, table *models.TableInfo, item *models.Item) error { -// -//} diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go index 42f7e31..fab7026 100644 --- a/internal/dynamo-browse/ui/model.go +++ b/internal/dynamo-browse/ui/model.go @@ -85,172 +85,12 @@ func NewModel( dialogPrompt := dialogprompt.New(statusAndPrompt) tableSelect := tableselect.New(dialogPrompt, uiStyles) - /* - cc.AddCommands(&commandctrl.CommandList{ - Commands: map[string]commandctrl.Command{ - "quit": commandctrl.NoArgCommand(tea.Quit), - "table": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var tableName string - if err := args.Bind(&tableName); err == nil { - return rc.ScanTable(tableName) - } - - return rc.ListTables(false) - }, - "export": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var filename string - if err := args.Bind(&filename); err != nil { - return events.Error(errors.New("expected filename")) - } - - opts := controllers.ExportOptions{ - AllResults: args.HasSwitch("all"), - } - - return exportController.ExportCSV(filename, opts) - }, - "mark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var markOp = controllers.MarkOpMark - - var markOpStr string - if err := args.Bind(&markOpStr); err == nil { - switch markOpStr { - case "all": - markOp = controllers.MarkOpMark - case "none": - markOp = controllers.MarkOpUnmark - case "toggle": - markOp = controllers.MarkOpToggle - default: - return events.Error(errors.New("unrecognised mark operation")) - } - } - - var whereExpr = "" - _ = args.BindSwitch("where", &whereExpr) - - return rc.Mark(markOp, whereExpr) - }, - "unmark": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return rc.Mark(controllers.MarkOpUnmark, "") - }, - "next-page": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return rc.NextPage() - }, - "delete": commandctrl.NoArgCommand(wc.DeleteMarked), - - // TEMP - "new-item": commandctrl.NoArgCommand(wc.NewItem), - "clone": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return wc.CloneItem(dtv.SelectedItemIndex()) - }, - "set-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var fieldName string - if err := args.Bind(&fieldName); err != nil { - return events.Error(errors.New("expected field")) - } - - var itemType = models.UnsetItemType - switch { - case args.HasSwitch("S"): - itemType = models.StringItemType - case args.HasSwitch("N"): - itemType = models.NumberItemType - case args.HasSwitch("BOOL"): - itemType = models.BoolItemType - case args.HasSwitch("NULL"): - itemType = models.NullItemType - case args.HasSwitch("TO"): - itemType = models.ExprValueItemType - } - - return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, fieldName) - }, - "del-attr": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var fieldName string - // TODO: support rest args - if err := args.Bind(&fieldName); err != nil { - return events.Error(errors.New("expected field")) - } - - return wc.DeleteAttribute(dtv.SelectedItemIndex(), fieldName) - }, - - "put": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return wc.PutItems() - }, - "touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return wc.TouchItem(dtv.SelectedItemIndex()) - }, - "noisy-touch": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - return wc.NoisyTouchItem(dtv.SelectedItemIndex()) - }, - - - "echo": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - s := new(strings.Builder) - for _, arg := range args { - s.WriteString(arg) - } - return events.StatusMsg(s.String()) - }, - "set-opt": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var name string - if err := args.Bind(&name); err != nil { - return events.Error(errors.New("expected settingName")) - } - - var value string - if err := args.Bind(&value); err == nil { - return settingsController.SetSetting(name, value) - } - - return settingsController.SetSetting(name, "") - }, - "rebind": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var bindingName, newKey string - if err := args.Bind(&bindingName, &newKey); err != nil { - return events.Error(errors.New("expected: bindingName newKey")) - } - - return keyBindingController.Rebind(bindingName, newKey, ctx.FromFile) - }, - - "run-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var name string - if err := args.Bind(&name); err != nil { - return events.Error(errors.New("expected: script name")) - } - - return scriptController.RunScript(name) - }, - "load-script": func(ctx commandctrl.ExecContext, args ucl.CallArgs) tea.Msg { - var name string - if err := args.Bind(&name); err != nil { - return events.Error(errors.New("expected: script name")) - } - - return scriptController.LoadScript(name) - }, - - // Aliases - "sa": cc.Alias("set-attr"), - "da": cc.Alias("del-attr"), - "np": cc.Alias("next-page"), - "w": cc.Alias("put"), - "q": cc.Alias("quit"), - }, - }) - - */ - root := layout.FullScreen(tableSelect) return Model{ tableReadController: rc, tableWriteController: wc, commandController: cc, - //scriptController: scriptController, exportController: exportController, jobController: jobController, itemEdit: itemEdit,