diff --git a/.github/workflows/coverage_runner.yaml b/.github/workflows/coverage_runner.yaml index 7c1f61e0..9629737e 100644 --- a/.github/workflows/coverage_runner.yaml +++ b/.github/workflows/coverage_runner.yaml @@ -44,9 +44,21 @@ jobs: - name: Install JDK uses: actions/setup-java@v2 with: - distribution: 'adopt' + distribution: 'zulu' java-version: '8' + - name: "Download RCD (Linux/MacOS)" + if: "!contains(matrix.os, 'windows')" + run: | + wget https://rcd-download.s3.us-east-2.amazonaws.com/rcd-${{ matrix.os }} + + - name: "Start Hazelcast Remote Controller (Linux/MacOS)" + if: "!contains(matrix.os, 'windows')" + run: | + chmod +x rcd-${{ matrix.os }} + ./rcd-${{ matrix.os }} -version $HZ_VERSION -dir $HOME & + sleep 10 + - name: Checkout code for PR if: github.event_name == 'pull_request_target' uses: actions/checkout@v2 @@ -65,10 +77,11 @@ jobs: - name: Test env: - HAZELCAST_ENTERPRISE_KEY: ${{ secrets.HAZELCAST_ENTERPRISE_KEY }} +# HAZELCAST_ENTERPRISE_KEY: ${{ secrets.HAZELCAST_ENTERPRISE_KEY }} HZ_VERSION: 5.2.1 - SSL_ENABLED: 1 +# SSL_ENABLED: 1 MEMBER_COUNT: 3 + DEFAULT_TIMEOUT: "30s" run: | ./rc.sh start go mod tidy diff --git a/.github/workflows/test-all.yaml b/.github/workflows/test-all.yaml index dd12f910..22621cf2 100644 --- a/.github/workflows/test-all.yaml +++ b/.github/workflows/test-all.yaml @@ -10,6 +10,7 @@ jobs: env: GOPATH: "${{ github.workspace }}" HZ_VERSION: "5.2.2" + DEFAULT_TIMEOUT: "30s" defaults: run: shell: "bash" @@ -23,8 +24,8 @@ jobs: - name: "Setup JRE" uses: actions/setup-java@v2 with: - distribution: zulu - java-version: 8 + distribution: "zulu" + java-version: "8" - name: "Download RCD (Linux/MacOS)" if: "!contains(matrix.os, 'windows')" diff --git a/Makefile b/Makefile index a461ca2e..42c12de0 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,10 @@ build: CGO_ENABLED=0 go build -tags base,hazelcastinternal,hazelcastinternaltest -ldflags $(LDFLAGS) -o build/$(BINARY_NAME) ./cmd/clc test: - go test -tags base,hazelcastinternal,hazelcastinternaltest $(TEST_FLAGS) ./... + go test -tags base,hazelcastinternal,hazelcastinternaltest -p 1 $(TEST_FLAGS) ./... test-cover: - go test -tags base,hazelcastinternal,hazelcastinternaltest $(TEST_FLAGS) -coverprofile=coverage.out -coverpkg $(PACKAGES) -coverprofile=$(COVERAGE_OUT) ./... + go test -tags base,hazelcastinternal,hazelcastinternaltest -p 1 $(TEST_FLAGS) -coverprofile=coverage.out -coverpkg $(PACKAGES) -coverprofile=$(COVERAGE_OUT) ./... view-cover: go tool cover -func $(COVERAGE_OUT) | grep total: diff --git a/base/commands/config/config_it_test.go b/base/commands/config/config_it_test.go new file mode 100644 index 00000000..9414d936 --- /dev/null +++ b/base/commands/config/config_it_test.go @@ -0,0 +1,67 @@ +package config_test + +import ( + "os" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hazelcast/hazelcast-commandline-client/clc/paths" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" +) + +func TestConfig(t *testing.T) { + testCases := []struct { + name string + f func(t *testing.T) + }{ + {name: "Import", f: importTest}, + {name: "Add", f: addTest}, + } + for _, tc := range testCases { + t.Run(tc.name, tc.f) + } +} + +func importTest(t *testing.T) { + tcx := it.TestContext{T: t} + const configURL = "https://rcd-download.s3.us-east-2.amazonaws.com/hazelcast-cloud-go-sample-client-pr-FOR_TESTING-default.zip" + tcx.Tester(func(tcx it.TestContext) { + name := it.NewUniqueObjectName("cfg") + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("config", "import", name, configURL)) + tcx.AssertStdoutContains("OK\n") + path := paths.Join(paths.ResolveConfigPath(name)) + tcx.T.Logf("config path: %s", path) + assert.True(tcx.T, paths.Exists(path)) + }) + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("config", "list")) + tcx.AssertStdoutContains(name) + }) + }) +} + +func addTest(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + name := it.NewUniqueObjectName("cfg") + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("config", "add", name, "cluster.address=foobar.com")) + tcx.AssertStdoutContains("OK\n") + }) + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("config", "list")) + tcx.AssertStdoutContains(name) + }) + path := paths.ResolveConfigPath(name) + require.True(tcx.T, paths.Exists(path)) + r := check.MustValue(regexp.Compile(`cluster:\n\s+address: foobar.com`)) + text := check.MustValue(os.ReadFile(path)) + t.Logf(string(text)) + require.True(tcx.T, r.Match(text)) + }) +} diff --git a/base/commands/map/map_get.go b/base/commands/map/map_get.go index c1fc8351..ac12f71e 100644 --- a/base/commands/map/map_get.go +++ b/base/commands/map/map_get.go @@ -52,7 +52,7 @@ func (mc *MapGetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { value, err := ci.DecodeData(raw) if err != nil { ec.Logger().Info("The value for %s was not decoded, due to error: %s", keyStr, err.Error()) - value = serialization.NondecodedType(serialization.TypeToString(vt)) + value = serialization.NondecodedType(serialization.TypeToLabel(vt)) } row := output.Row{ output.Column{ @@ -65,7 +65,7 @@ func (mc *MapGetCommand) Exec(ctx context.Context, ec plug.ExecContext) error { row = append(row, output.Column{ Name: output.NameValueType, Type: serialization.TypeString, - Value: serialization.TypeToString(vt), + Value: serialization.TypeToLabel(vt), }) } return ec.AddOutputRows(ctx, row) diff --git a/base/commands/map/map_test.go b/base/commands/map/map_it_test.go similarity index 55% rename from base/commands/map/map_test.go rename to base/commands/map/map_it_test.go index 8eca488f..4150a211 100644 --- a/base/commands/map/map_test.go +++ b/base/commands/map/map_it_test.go @@ -18,8 +18,10 @@ func TestMap(t *testing.T) { name string f func(t *testing.T) }{ + {name: "Clear_NonInteractive", f: clear_NonInteractiveTest}, {name: "EntrySet_NonInteractive", f: entrySet_NonInteractiveTest}, {name: "Get_Noninteractive", f: get_NonInteractiveTest}, + {name: "Remove_Noninteractive", f: remove_NonInteractiveTest}, {name: "Set_NonInteractive", f: set_NonInteractiveTest}, {name: "Size_Interactive", f: size_InteractiveTest}, {name: "Size_Noninteractive", f: size_NoninteractiveTest}, @@ -29,51 +31,78 @@ func TestMap(t *testing.T) { } } -func entrySet_NonInteractiveTest(t *testing.T) { - mapTester(t, func(tcx it.TestContext, m *hz.Map) { +func clear_NonInteractiveTest(t *testing.T) { + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { t := tcx.T + ctx := context.Background() + tcx.WithReset(func() { + check.Must(m.Set(ctx, "foo", "bar")) + require.Equal(t, 1, check.MustValue(m.Size(ctx))) + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "clear", "--quite")) + require.Equal(t, 0, check.MustValue(m.Size(ctx))) + }) + }) + +} + +func entrySet_NonInteractiveTest(t *testing.T) { + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { // no entry tcx.WithReset(func() { check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "entry-set", "--quite")) - tcx.AssertStdoutEquals(t, "") + tcx.AssertStdoutEquals("") }) // set an entry tcx.WithReset(func() { check.Must(m.Set(context.Background(), "foo", "bar")) check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "entry-set", "--quite")) - tcx.AssertStdoutContains(t, "foo\tbar\n") + tcx.AssertStdoutContains("foo\tbar\n") }) // show type tcx.WithReset(func() { check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "entry-set", "--show-type", "--quite")) - tcx.AssertStdoutContains(t, "foo\tSTRING\tbar\tSTRING\n") + tcx.AssertStdoutContains("foo\tSTRING\tbar\tSTRING\n") }) }) } func get_NonInteractiveTest(t *testing.T) { - mapTester(t, func(tcx it.TestContext, m *hz.Map) { - t := tcx.T + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { // no entry tcx.WithReset(func() { check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "foo", "--quite")) - tcx.AssertStdoutEquals(t, "-\n") + tcx.AssertStdoutEquals("-\n") }) // set an entry tcx.WithReset(func() { check.Must(m.Set(context.Background(), "foo", "bar")) - check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "foo", "--quite")) - tcx.AssertStdoutContains(t, "bar\n") + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "foo", "--quite", "--show-type")) + tcx.AssertStdoutEquals("bar\tSTRING\n") + }) + }) +} + +func remove_NonInteractiveTest(t *testing.T) { + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { + ctx := context.Background() + tcx.WithReset(func() { + check.Must(m.Set(ctx, "foo", "bar")) + size := check.MustValue(m.Size(ctx)) + require.Equal(tcx.T, 1, size) + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "remove", "foo", "--quite", "--show-type")) + tcx.AssertStdoutEquals("bar\tSTRING\n") + size = check.MustValue(m.Size(ctx)) + require.Equal(tcx.T, 0, size) }) }) } func set_NonInteractiveTest(t *testing.T) { - mapTester(t, func(tcx it.TestContext, m *hz.Map) { + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { t := tcx.T tcx.WithReset(func() { tcx.CLCExecute("map", "-n", m.Name(), "set", "foo", "bar", "--quite") - tcx.AssertStderrEquals(t, "") + tcx.AssertStderrEquals("") v := check.MustValue(m.Get(context.Background(), "foo")) require.Equal(t, "bar", v) }) @@ -81,54 +110,35 @@ func set_NonInteractiveTest(t *testing.T) { } func size_NoninteractiveTest(t *testing.T) { - mapTester(t, func(tcx it.TestContext, m *hz.Map) { - t := tcx.T + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { // no entry tcx.WithReset(func() { check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "size", "--quite")) - tcx.AssertStdoutEquals(t, "0\n") + tcx.AssertStdoutEquals("0\n") }) // set an entry tcx.WithReset(func() { - tcx.AssertStdoutEquals(t, "") + tcx.AssertStdoutEquals("") check.Must(m.Set(context.Background(), "foo", "bar")) check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "size", "--quite")) - tcx.AssertStdoutEquals(t, "1\n") + tcx.AssertStdoutEquals("1\n") }) }) } func size_InteractiveTest(t *testing.T) { - mapTester(t, func(tcx it.TestContext, m *hz.Map) { - t := tcx.T + it.MapTester(t, func(tcx it.TestContext, m *hz.Map) { ctx := context.Background() - go func(t *testing.T) { - check.Must(tcx.CLC().Execute()) - }(t) - tcx.WithReset(func() { - tcx.WriteStdin([]byte(fmt.Sprintf("\\map -n %s size\n", m.Name()))) - tcx.AssertStdoutDollarWithPath(t, "testdata/map_size_0.txt") - }) - tcx.WithReset(func() { - check.Must(m.Set(ctx, "foo", "bar")) - tcx.WriteStdin([]byte(fmt.Sprintf("\\map -n %s size\n", m.Name()))) - tcx.AssertStdoutDollarWithPath(t, "testdata/map_size_1.txt") - }) - }) -} - -func withMap(tcx it.TestContext, fn func(m *hz.Map)) { - name := it.NewUniqueObjectName("map") - ctx := context.Background() - m := check.MustValue(tcx.Client.GetMap(ctx, name)) - fn(m) -} - -func mapTester(t *testing.T, fn func(tcx it.TestContext, m *hz.Map)) { - tcx := it.TestContext{T: t} - tcx.Tester(func(tcx it.TestContext) { - withMap(tcx, func(m *hz.Map) { - fn(tcx, m) + tcx.WithShell(func(tcx it.TestContext) { + tcx.WithReset(func() { + tcx.WriteStdin([]byte(fmt.Sprintf("\\map -n %s size\n", m.Name()))) + tcx.AssertStdoutDollarWithPath("testdata/map_size_0.txt") + }) + tcx.WithReset(func() { + check.Must(m.Set(ctx, "foo", "bar")) + tcx.WriteStdin([]byte(fmt.Sprintf("\\map -n %s size\n", m.Name()))) + tcx.AssertStdoutDollarWithPath("testdata/map_size_1.txt") + }) }) }) } diff --git a/base/commands/map/map_remove.go b/base/commands/map/map_remove.go index 1ea24994..6c2317fd 100644 --- a/base/commands/map/map_remove.go +++ b/base/commands/map/map_remove.go @@ -52,7 +52,7 @@ func (mc *MapRemoveCommand) Exec(ctx context.Context, ec plug.ExecContext) error value, err := ci.DecodeData(raw) if err != nil { ec.Logger().Info("The value for %s was not decoded, due to error: %s", keyStr, err.Error()) - value = serialization.NondecodedType(serialization.TypeToString(vt)) + value = serialization.NondecodedType(serialization.TypeToLabel(vt)) } row := output.Row{ output.Column{ @@ -65,7 +65,7 @@ func (mc *MapRemoveCommand) Exec(ctx context.Context, ec plug.ExecContext) error row = append(row, output.Column{ Name: output.NameValueType, Type: serialization.TypeString, - Value: serialization.TypeToString(vt), + Value: serialization.TypeToLabel(vt), }) } return ec.AddOutputRows(ctx, row) diff --git a/base/commands/shell.go b/base/commands/shell.go index aaa4dd53..2ad7fbfa 100644 --- a/base/commands/shell.go +++ b/base/commands/shell.go @@ -141,7 +141,7 @@ func (cm *ShellCommand) ExecInteractive(ctx context.Context, ec plug.ExecContext Stderr: ec.Stderr(), Stdout: ec.Stdout(), } - sh := shell.NewOneshot(endLineFn, sio, textFn) + sh := shell.NewOneshotShell(endLineFn, sio, textFn) sh.SetCommentPrefix("--") return sh.Run(context.Background()) } diff --git a/base/commands/sql/common.go b/base/commands/sql/common.go index 2b43bf19..591c7e15 100644 --- a/base/commands/sql/common.go +++ b/base/commands/sql/common.go @@ -91,7 +91,7 @@ func ExecSQL(ctx context.Context, ec plug.ExecContext, query string) (sql.Result // Once that is removed from the Go client, the code below may be removed. err = AdaptSQLError(err) if !as { - if serr.Suggestion != "" { + if serr.Suggestion != "" && !ec.Interactive() { return nil, stop, fmt.Errorf("%w\n\nUse --%s to automatically apply the suggestion", err, propertyUseMappingSuggestion) } return nil, stop, err diff --git a/base/commands/sql/sql_it_test.go b/base/commands/sql/sql_it_test.go new file mode 100644 index 00000000..19f5ce01 --- /dev/null +++ b/base/commands/sql/sql_it_test.go @@ -0,0 +1,192 @@ +package sql_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hazelcast/hazelcast-go-client" + + _ "github.com/hazelcast/hazelcast-commandline-client/base/commands" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" +) + +func TestSQL(t *testing.T) { + testCases := []struct { + name string + f func(t *testing.T) + }{ + {name: "SQLOutput_NonInteractive", f: sqlOutput_NonInteractiveTest}, + {name: "SQL_ShellCommand", f: sql_shellCommandTest}, + {name: "SQL_Interactive", f: sql_InteractiveTest}, + {name: "SQL_NonInteractive", f: sql_NonInteractiveTest}, + {name: "SQL_Suggestion_Interactive", f: sqlSuggestion_Interactive}, + {name: "SQL_Suggestion_NonInteractive", f: sqlSuggestion_NonInteractive}, + } + for _, tc := range testCases { + t.Run(tc.name, tc.f) + } +} + +func sql_NonInteractiveTest(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + name := it.NewUniqueObjectName("table") + tcx.CLCExecute("sql", fmt.Sprintf(` + CREATE MAPPING "%s" ( + __key INT, + this VARCHAR + ) TYPE IMAP OPTIONS ( + 'keyFormat' = 'int', + 'valueFormat' = 'varchar' + ); + `, name)) + tcx.CLCExecute("sql", fmt.Sprintf(` + INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); + `, name)) + tcx.WithReset(func() { + tcx.CLCExecute("sql", fmt.Sprintf(` + SELECT * FROM "%s" ORDER BY __key; + `, name)) + tcx.AssertStdoutContains("10\tfoo\n20\tbar\n") + }) + }) +} + +func sql_InteractiveTest(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + tcx.WithShell(func(tcx it.TestContext) { + name := it.NewUniqueObjectName("table") + tcx.WriteStdinF(` + CREATE MAPPING "%s" ( + __key INT, + this VARCHAR + ) TYPE IMAP OPTIONS ( + 'keyFormat' = 'int', + 'valueFormat' = 'varchar' + );`+"\n", name) + tcx.WriteStdinF(` + INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); + `+"\n", name) + tcx.WithReset(func() { + tcx.WriteStdinF(`SELECT * FROM "%s" ORDER BY __key;`+"\n", name) + tcx.AssertStdoutDollarWithPath("testdata/sql_1.txt") + }) + }) + }) +} + +func sql_shellCommandTest(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + ctx := context.Background() + tcx.WithShell(func(tcx it.TestContext) { + // help + tcx.WithReset(func() { + tcx.WriteStdinString("help\n") + tcx.AssertStdoutDollarWithPath("testdata/sql_help.txt") + }) + name := it.NewUniqueObjectName("table")[:16] + q := fmt.Sprintf(` + CREATE MAPPING "%s" ( + __key INT, + this VARCHAR + ) TYPE IMAP OPTIONS ( + 'keyFormat' = 'int', + 'valueFormat' = 'varchar' + );`+"\n", name) + check.MustValue(tcx.Client.SQL().Execute(ctx, q)) + // dm + tcx.WithReset(func() { + tcx.WriteStdinString("\\dm\n") + tcx.AssertStdoutContains(name) + }) + // dm NAME + tcx.WithReset(func() { + tcx.WriteStdinF("\\dm %s\n", name) + target := fmt.Sprintf(`$----------------------------------------------------------------------------------------------------$ +$ table_catalog | table_schema | table_name | mapping_external_name | mapping_type | mapping_options $ +$----------------------------------------------------------------------------------------------------$ +$ hazelcast | public | test-table | %s | IMAP | {"keyFormat":"i $ +$----------------------------------------------------------------------------------------------------$`, name) + tcx.AssertStdoutDollar(target) + }) + // dm+ NAME + tcx.WithReset(func() { + tcx.WriteStdinF("\\dm+ %s\n", name) + target := `$-----------------------------------------------------------------------------------------------------------------------------$ +$ table_catalog | table_schema | table_name | column_name | column_external_name | ordinal_position | is_nullable | data_type $ +$-----------------------------------------------------------------------------------------------------------------------------$ +$ hazelcast | public | test-table | __key | __key | 1 | true | INTEGER $ +$ hazelcast | public | test-table | this | this | 2 | true | VARCHAR $ +$-----------------------------------------------------------------------------------------------------------------------------$` + tcx.AssertStdoutDollar(target) + }) + }) + }) +} + +func sqlSuggestion_Interactive(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "foo", "bar")) + tcx.WithShell(func(tcx it.TestContext) { + tcx.WriteStdinF(`SELECT * FROM "%s";`+"\n", m.Name()) + tcx.AssertStderrContains("CREATE MAPPING") + tcx.AssertStderrNotContains("--use-mapping-suggestion") + }) + }) + }) +} + +func sqlSuggestion_NonInteractive(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "foo", "bar")) + // ignoring the error here + _ = tcx.CLC().Execute("sql", fmt.Sprintf(`SELECT * FROM "%s";`, m.Name())) + tcx.AssertStderrContains("CREATE MAPPING") + tcx.AssertStderrContains("--use-mapping-suggestion") + check.Must(tcx.CLC().Execute("sql", fmt.Sprintf(`SELECT * FROM "%s";`, m.Name()), "--use-mapping-suggestion")) + tcx.AssertStdoutContains("foo\tbar") + }) + }) +} + +func sqlOutput_NonInteractiveTest(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + name := it.NewUniqueObjectName("table") + ctx := context.Background() + check.MustValue(tcx.Client.SQL().Execute(ctx, fmt.Sprintf(` + CREATE MAPPING "%s" ( + __key INT, + this VARCHAR + ) TYPE IMAP OPTIONS ( + 'keyFormat' = 'int', + 'valueFormat' = 'varchar' + ); + `, name))) + check.MustValue(tcx.Client.SQL().Execute(ctx, fmt.Sprintf(` + INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); + `, name))) + testCases := []string{"delimited", "json", "csv", "table"} + for _, f := range testCases { + f := f + tcx.T.Run(f, func(t *testing.T) { + tcx.WithReset(func() { + tcx.CLCExecute("sql", "--format", f, "--quite", fmt.Sprintf(` + SELECT * FROM "%s" ORDER BY __key; + `, name)) + tcx.AssertStdoutDollarWithPath(fmt.Sprintf("testdata/sql_output_%s.txt", f)) + }) + }) + } + }) +} diff --git a/base/commands/sql/sql_test.go b/base/commands/sql/sql_test.go deleted file mode 100644 index 68ff3254..00000000 --- a/base/commands/sql/sql_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package sql_test - -import ( - "context" - "fmt" - "testing" - - _ "github.com/hazelcast/hazelcast-commandline-client/base/commands" - "github.com/hazelcast/hazelcast-commandline-client/internal/check" - "github.com/hazelcast/hazelcast-commandline-client/internal/it" -) - -func TestSQL(t *testing.T) { - testCases := []struct { - name string - f func(t *testing.T) - }{ - {name: "SQLOutput_NonInteractive", f: sqlOutput_NonInteractiveTest}, - {name: "SQL_Interactive", f: sql_InteractiveTest}, - {name: "SQL_NonInteractive", f: sql_NonInteractiveTest}, - } - for _, tc := range testCases { - t.Run(tc.name, tc.f) - } -} - -func sql_NonInteractiveTest(t *testing.T) { - tcx := it.TestContext{T: t} - tcx.Tester(func(tcx it.TestContext) { - t := tcx.T - name := it.NewUniqueObjectName("table") - check.Must(tcx.CLC().Execute("sql", fmt.Sprintf(` - CREATE MAPPING "%s" ( - __key INT, - this VARCHAR - ) TYPE IMAP OPTIONS ( - 'keyFormat' = 'int', - 'valueFormat' = 'varchar' - ); - `, name))) - check.Must(tcx.CLC().Execute("sql", fmt.Sprintf(` - INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); - `, name))) - tcx.WithReset(func() { - check.Must(tcx.CLC().Execute("sql", fmt.Sprintf(` - SELECT * FROM "%s" ORDER BY __key; - `, name))) - tcx.AssertStdoutContains(t, "10\tfoo\n20\tbar\n") - }) - }) -} - -func sql_InteractiveTest(t *testing.T) { - tcx := it.TestContext{T: t} - tcx.Tester(func(tcx it.TestContext) { - t := tcx.T - go func(t *testing.T) { - check.Must(tcx.CLC().Execute()) - }(t) - name := it.NewUniqueObjectName("table") - tcx.WriteStdin([]byte(fmt.Sprintf(` - CREATE MAPPING "%s" ( - __key INT, - this VARCHAR - ) TYPE IMAP OPTIONS ( - 'keyFormat' = 'int', - 'valueFormat' = 'varchar' - );`+"\n", name))) - tcx.WriteStdin([]byte(fmt.Sprintf(` - INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); - `+"\n", name))) - tcx.WithReset(func() { - tcx.WriteStdin([]byte(fmt.Sprintf(` - SELECT * FROM "%s" ORDER BY __key; - `+"\n", name))) - tcx.AssertStdoutDollarWithPath(t, "testdata/sql_1.txt") - }) - }) -} - -func sqlOutput_NonInteractiveTest(t *testing.T) { - tcx := it.TestContext{T: t} - tcx.Tester(func(tcx it.TestContext) { - t := tcx.T - name := it.NewUniqueObjectName("table") - ctx := context.Background() - check.MustValue(tcx.Client.SQL().Execute(ctx, fmt.Sprintf(` - CREATE MAPPING "%s" ( - __key INT, - this VARCHAR - ) TYPE IMAP OPTIONS ( - 'keyFormat' = 'int', - 'valueFormat' = 'varchar' - ); - `, name))) - check.MustValue(tcx.Client.SQL().Execute(ctx, fmt.Sprintf(` - INSERT INTO "%s" (__key, this) VALUES (10, 'foo'), (20, 'bar'); - `, name))) - testCases := []string{"delimited", "json", "csv", "table"} - for _, f := range testCases { - f := f - t.Run(f, func(t *testing.T) { - tcx.WithReset(func() { - check.Must(tcx.CLC().Execute("sql", "--format", f, "--quite", fmt.Sprintf(` - SELECT * FROM "%s" ORDER BY __key; - `, name))) - p := fmt.Sprintf("testdata/sql_output_%s.txt", f) - tcx.AssertStdoutDollarWithPath(t, p) - }) - }) - } - }) -} diff --git a/base/commands/sql/testdata/sql_help.txt b/base/commands/sql/testdata/sql_help.txt new file mode 100644 index 00000000..641d5073 --- /dev/null +++ b/base/commands/sql/testdata/sql_help.txt @@ -0,0 +1,6 @@ +$Shortcut Commands:$ +$\dm List mappings$ +$\dm MAPPING Display information about a mapping$ +$\dm+ MAPPING Describe a mapping$ +$\exit Exit the shell$ +$\help Display help for CLC commands$ diff --git a/base/commands/version_test.go b/base/commands/version_test.go index e33435f6..de3d2ca5 100644 --- a/base/commands/version_test.go +++ b/base/commands/version_test.go @@ -24,9 +24,7 @@ func TestVersion(t *testing.T) { ec := it.NewExecuteContext(nil) ec.Set(clc.PropertyFormat, base.PrinterDelimited) require.NoError(t, cmd.Exec(context.TODO(), ec)) - output := ec.StdoutText() - t.Log("output", output) - assert.Equal(t, "v5.2.0\n", output) + assert.Equal(t, "v5.2.0\n", ec.StdoutText()) ec.Set(clc.PropertyVerbose, true) require.NoError(t, cmd.Exec(context.TODO(), ec)) assert.Equal(t, ec.Rows[0][0].Value, "Hazelcast CLC") diff --git a/base/home_test.go b/base/home_test.go new file mode 100644 index 00000000..1660372c --- /dev/null +++ b/base/home_test.go @@ -0,0 +1,60 @@ +package base_test + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/hazelcast/hazelcast-commandline-client/base/commands" + "github.com/hazelcast/hazelcast-commandline-client/clc/paths" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" + "github.com/hazelcast/hazelcast-commandline-client/internal/it/skip" +) + +func TestHome(t *testing.T) { + testCases := []struct { + name string + f func(t *testing.T) + }{ + {name: "home", f: homeTest_Unix}, + {name: "homeWithEnv", f: homeWithEnvTest}, + } + for _, tc := range testCases { + t.Run(tc.name, tc.f) + } +} + +func homeTest_Unix(t *testing.T) { + skip.If(t, "os = windows") + homeTester(t, nil, func(t *testing.T, ec *it.ExecContext) { + output := ec.StdoutText() + target := check.MustValue(os.UserHomeDir()) + "/.hazelcast\n" + assert.Equal(t, target, output) + }) +} + +// TODO: TestHome_Windows + +func homeWithEnvTest(t *testing.T) { + skip.If(t, "os = windows") + it.WithEnv(paths.EnvCLCHome, "/home/foo/dir", func() { + homeTester(t, nil, func(t *testing.T, ec *it.ExecContext) { + output := ec.StdoutText() + target := "/home/foo/dir\n" + assert.Equal(t, target, output) + }) + }) +} + +func homeTester(t *testing.T, args []string, f func(t *testing.T, ec *it.ExecContext)) { + cmd := &commands.HomeCommand{} + cc := &it.CommandContext{} + require.NoError(t, cmd.Init(cc)) + ec := it.NewExecuteContext(args) + require.NoError(t, cmd.Exec(context.Background(), ec)) + f(t, ec) +} diff --git a/clc/cmd/clc.go b/clc/cmd/clc.go index 19f27773..1daa017f 100644 --- a/clc/cmd/clc.go +++ b/clc/cmd/clc.go @@ -135,6 +135,8 @@ func (m *Main) Root() *cobra.Command { return m.root } +// TODO: add context arg to Execute + func (m *Main) Execute(args ...string) error { var cm *cobra.Command var cmdArgs []string @@ -390,6 +392,6 @@ func convertFlagValue(fs *pflag.FlagSet, name string, v pflag.Value) any { } func init() { - hazelcast.SetDefaultCompactDeserializer(serialization.NewGenericCompactDeserializer()) + hazelcast.SetDefaultCompactDeserializer(serialization.GenericCompactDeserializer{}) hazelcast.SetDefaultPortableDeserializer(serialization.NewGenericPortableSerializer()) } diff --git a/clc/cmd/exec_context.go b/clc/cmd/exec_context.go index e318168e..3540b0f1 100644 --- a/clc/cmd/exec_context.go +++ b/clc/cmd/exec_context.go @@ -7,7 +7,6 @@ import ( "io" "os" "os/signal" - "strings" "time" "github.com/fatih/color" @@ -239,9 +238,6 @@ func (ec *ExecContext) Wrap(f func() error) error { } else { var msg string errStr := err.Error() - if ec.Interactive() { - errStr = trimError(err, maxErrorLines) - } if verbose { msg = fmt.Sprintf("\nError in %d ms: %s", took.Milliseconds(), errStr) } else { @@ -302,12 +298,3 @@ func (n nopSpinner) Start() error { func (n nopSpinner) SetText(text string) { // pass } - -// trimErrorString trims the string so it's at most n lines -func trimError(err error, n int) string { - lines := strings.Split(err.Error(), "\n") - if len(lines) > n { - lines = append(lines[:5], "(Rest of the error message is trimmed.)") - } - return strings.Join(lines, "\n") -} diff --git a/clc/config/import.go b/clc/config/import.go index 84dc04b2..f7f4b35e 100644 --- a/clc/config/import.go +++ b/clc/config/import.go @@ -29,6 +29,15 @@ func ImportSource(ctx context.Context, ec plug.ExecContext, target, src string) if ok { return path, nil } + // import is not successful, check whether this an HTTP source + path, ok, err = tryImportHTTPSource(ctx, ec, target, src) + if err != nil { + return "", err + } + // import is successful + if ok { + return path, nil + } // import is not successful, so assume this is a zip file path and try to import from it. path, ok, err = tryImportViridianZipSource(ctx, ec, target, src) if err != nil { @@ -42,19 +51,22 @@ func ImportSource(ctx context.Context, ec plug.ExecContext, target, src string) // tryImportViridianCurlSource returns true if importing from a Viridian CURL command line is successful func tryImportViridianCurlSource(ctx context.Context, ec plug.ExecContext, target, src string) (string, bool, error) { - const reCurlSource = `curl (?P.*) -o hazelcast-cloud-(?P[a-z]+)-sample-client-(?P[a-z-0-9-]+)-default\.zip` + const reCurlSource = `curl (?P[^\s]+)\s+` re, err := regexp.Compile(reCurlSource) if err != nil { return "", false, err } grps := re.FindStringSubmatch(src) - if len(grps) != 4 { + if len(grps) < 2 { return "", false, nil } url := grps[1] - language := grps[2] - if language != "go" { - return "", false, fmt.Errorf("%s sample is not usable as a configuration source, use Go sample", language) + return tryImportHTTPSource(ctx, ec, target, url) +} + +func tryImportHTTPSource(ctx context.Context, ec plug.ExecContext, target, url string) (string, bool, error) { + if !strings.HasPrefix(url, "https://") && !strings.HasSuffix(url, "http://") { + return "", false, nil } path, err := download(ctx, ec, url) if err != nil { @@ -66,11 +78,12 @@ func tryImportViridianCurlSource(ctx context.Context, ec plug.ExecContext, targe return "", false, err } return path, true, nil + } // tryImportViridianZipSource returns true if importing from a Viridian Go sample zip file is successful func tryImportViridianZipSource(ctx context.Context, ec plug.ExecContext, target, src string) (string, bool, error) { - const reSource = `hazelcast-cloud-(?P[a-z]+)-sample-client-(?P[a-z-0-9-]+)-default\.zip` + const reSource = `hazelcast-cloud-(?P[a-z]+)-sample-client-(?P[a-zA-Z0-9_-]+)-default\.zip` re, err := regexp.Compile(reSource) if err != nil { return "", false, err diff --git a/clc/shell/oneshot_shell.go b/clc/shell/oneshot_shell.go index b9b0220c..7d7b9de0 100644 --- a/clc/shell/oneshot_shell.go +++ b/clc/shell/oneshot_shell.go @@ -18,7 +18,7 @@ type OneshotShell struct { stdin io.Reader } -func NewOneshot(endLineFn EndLineFn, sio clc.IO, textFn TextFn) *OneshotShell { +func NewOneshotShell(endLineFn EndLineFn, sio clc.IO, textFn TextFn) *OneshotShell { return &OneshotShell{ endLineFn: endLineFn, textFn: textFn, diff --git a/go.mod b/go.mod index e00fc602..f63172b8 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,11 @@ require ( github.com/alecthomas/chroma v0.10.0 github.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/hazelcast/hazelcast-go-client v1.4.1-0.20230329124727-d0ecca63538d + github.com/hazelcast/hazelcast-go-client v1.4.0 github.com/mattn/go-runewidth v0.0.14 github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d github.com/spf13/cobra v1.6.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.0 github.com/theckman/yacspin v0.13.12 go.uber.org/zap v1.23.0 @@ -25,33 +24,27 @@ require ( github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/chzyer/logex v1.1.10 // indirect + github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-ole/go-ole v1.2.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.1 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect github.com/shirou/gopsutil/v3 v3.21.5 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect github.com/tklauser/go-sysconf v0.3.4 // indirect github.com/tklauser/numcpus v0.2.1 // indirect go.uber.org/atomic v1.7.0 // indirect @@ -60,7 +53,7 @@ require ( golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.4.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) @@ -74,3 +67,5 @@ require ( github.com/nyaosorg/go-readline-ny v0.8.3 gopkg.in/yaml.v2 v2.4.0 ) + +replace github.com/hazelcast/hazelcast-go-client v1.4.0 => github.com/yuce/hazelcast-go-client v1.1.2-0.20230404141417-cffc4cae7feb diff --git a/go.sum b/go.sum index 8720d710..8bba5bad 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= @@ -57,7 +17,6 @@ github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= @@ -69,121 +28,39 @@ github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZ github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c h1:yE35fKFwcelIte3q5q1/cPiY7pI7vvf5/j/0ddxNCKs= github.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c/go.mod h1:9S/fKAutQ6wVHqm1jnp9D9sc5hu689s9AaTWFS92LaU= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hazelcast/hazelcast-go-client v1.4.1-0.20230329124727-d0ecca63538d h1:clvXDLbvF8mE9KK7Bwiyg+r6grbz4+E1zOU8JRXl3sA= -github.com/hazelcast/hazelcast-go-client v1.4.1-0.20230329124727-d0ecca63538d/go.mod h1:PJ38lqXJ18S0YpkrRznPDlUH8GnnMAQCx3jpQtBPZ6Q= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -199,8 +76,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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -217,21 +92,15 @@ github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d h github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d/go.mod h1:cxIIfNMTwff8f/ZvRouvWYF6wOoO7nj99neWSx2q/Es= github.com/nyaosorg/go-readline-ny v0.8.3 h1:aIxKpx7pH5Rc/zZk24v5C8NFYxFYEbh79zTaLnbLido= github.com/nyaosorg/go-readline-ny v0.8.3/go.mod h1:aoTnObwqS04NL0T9zwUvIO8h7v8fcKawdzJXRnV97T4= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= @@ -239,47 +108,26 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.21.5 h1:YUBf0w/KPLk7w1803AYBnH7BmA+1Z/Q5MEZxpREUaB4= github.com/shirou/gopsutil/v3 v3.21.5/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= 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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/theckman/yacspin v0.13.12 h1:CdZ57+n0U6JMuh2xqjnjRq5Haj6v1ner2djtLQRzJr4= github.com/theckman/yacspin v0.13.12/go.mod h1:Rd2+oG2LmQi5f3zC3yeZAOl245z8QOvrH4OPOJNZxLg= github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc= github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +github.com/yuce/hazelcast-go-client v1.1.2-0.20230404141417-cffc4cae7feb h1:rphhdpvUU8rEqvzKu5VMH62y+bQyQTRcPEih8zqmvBc= +github.com/yuce/hazelcast-go-client v1.1.2-0.20230404141417-cffc4cae7feb/go.mod h1:PJ38lqXJ18S0YpkrRznPDlUH8GnnMAQCx3jpQtBPZ6Q= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -287,317 +135,33 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20221108223516-5d533826c662 h1:QIza2Vre5WI+NE5AQ6Wi2nGDgDOckLCHJdhcM/kxcfw= golang.org/x/exp v0.0.0-20221108223516-5d533826c662/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/check/util.go b/internal/check/util.go new file mode 100644 index 00000000..08775078 --- /dev/null +++ b/internal/check/util.go @@ -0,0 +1,17 @@ +package check + +import "reflect" + +func IsNil(v any) bool { + if v == nil { + return true + } + vv := reflect.ValueOf(v) + switch vv.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, + reflect.Pointer, reflect.UnsafePointer, + reflect.Interface, reflect.Slice: + return vv.IsNil() + } + return false +} diff --git a/internal/it/context.go b/internal/it/context.go index 3f3965e3..3e646658 100644 --- a/internal/it/context.go +++ b/internal/it/context.go @@ -164,7 +164,7 @@ func (ec *ExecContext) StderrText() string { } func (ec *ExecContext) LoggerText() string { - return ec.lg.Text() + return ec.lg.String() } func (ec *ExecContext) Set(name string, value any) { diff --git a/internal/it/expect/expect.go b/internal/it/expect/expect.go index 719880c6..e989e27c 100644 --- a/internal/it/expect/expect.go +++ b/internal/it/expect/expect.go @@ -60,13 +60,15 @@ func (e *Expect) String() string { } func (e *Expect) Match(m Matcher, options ...Option) bool { - //e.Reset() o := Options{} for _, opt := range options { if err := opt(&o); err != nil { panic(fmt.Errorf("creating Match options: %w", err)) } } + if o.delay > 0 { + time.Sleep(o.delay) + } ch := make(chan struct{}) var done atomic.Bool go func() { diff --git a/internal/it/expect/matchers.go b/internal/it/expect/matchers.go index c368dc66..679e837a 100644 --- a/internal/it/expect/matchers.go +++ b/internal/it/expect/matchers.go @@ -57,7 +57,12 @@ func (m DollarMatcher) normalize(s string) string { scn := bufio.NewScanner(strings.NewReader(s)) for scn.Scan() { line := strings.TrimSpace(scn.Text()) + line = strings.ReplaceAll(line, `\`, `\\`) + line = strings.ReplaceAll(line, `+`, `\+`) + line = strings.ReplaceAll(line, `*`, `\*`) line = strings.ReplaceAll(line, "$", "\\s*") + line = strings.ReplaceAll(line, "[", "\\[") + line = strings.ReplaceAll(line, "|", "\\|") lines = append(lines, line) } return strings.Join(lines, "\n") diff --git a/internal/it/expect/options.go b/internal/it/expect/options.go index bb653d3a..7b4b8cec 100644 --- a/internal/it/expect/options.go +++ b/internal/it/expect/options.go @@ -6,6 +6,7 @@ type Option func(opts *Options) error type Options struct { timeout time.Duration + delay time.Duration } func WithTimeout(timeout time.Duration) Option { @@ -14,3 +15,10 @@ func WithTimeout(timeout time.Duration) Option { return nil } } + +func WithDelay(delay time.Duration) Option { + return func(opts *Options) error { + opts.delay = delay + return nil + } +} diff --git a/internal/it/logger.go b/internal/it/logger.go index 8aa72f32..6853ad36 100644 --- a/internal/it/logger.go +++ b/internal/it/logger.go @@ -23,7 +23,7 @@ func NewLogger() *Logger { return &Logger{buf: &bytes.Buffer{}} } -func (l Logger) Text() string { +func (l Logger) String() string { b, err := io.ReadAll(l.buf) if err != nil { panic(err) diff --git a/internal/it/map_tester.go b/internal/it/map_tester.go new file mode 100644 index 00000000..96f14b4c --- /dev/null +++ b/internal/it/map_tester.go @@ -0,0 +1,26 @@ +package it + +import ( + "context" + "testing" + + hz "github.com/hazelcast/hazelcast-go-client" + + "github.com/hazelcast/hazelcast-commandline-client/internal/check" +) + +func WithMap(tcx TestContext, fn func(m *hz.Map)) { + name := NewUniqueObjectName("map") + ctx := context.Background() + m := check.MustValue(tcx.Client.GetMap(ctx, name)) + fn(m) +} + +func MapTester(t *testing.T, fn func(tcx TestContext, m *hz.Map)) { + tcx := TestContext{T: t} + tcx.Tester(func(tcx TestContext) { + WithMap(tcx, func(m *hz.Map) { + fn(tcx, m) + }) + }) +} diff --git a/internal/it/test_context.go b/internal/it/test_context.go index 1ebded29..40541005 100644 --- a/internal/it/test_context.go +++ b/internal/it/test_context.go @@ -38,7 +38,10 @@ import ( "github.com/hazelcast/hazelcast-commandline-client/internal/it/expect" ) -const DefaultTimeout = 30 * time.Second +const ( + EnvDefaultTimeout = "DEFAULT_TIMEOUT" + DefaultDelay = 10 * time.Millisecond +) type TestContext struct { T *testing.T @@ -74,9 +77,7 @@ func (tcx TestContext) Stdout() *bytes.Buffer { } func (tcx TestContext) Stdin() io.Reader { - //panic("foo") return tcx.stdinR - //return tcx.stdin } func (tcx TestContext) CLC() *cmd.Main { @@ -97,6 +98,14 @@ func (tcx TestContext) WriteStdin(b []byte) { } } +func (tcx TestContext) WriteStdinString(s string) { + tcx.WriteStdin([]byte(s)) +} + +func (tcx TestContext) WriteStdinF(format string, args ...any) { + tcx.WriteStdin([]byte(fmt.Sprintf(format, args...))) +} + func (tcx TestContext) Tester(f func(tcx TestContext)) { ensureRemoteController(true) runner := func(tcx TestContext) { @@ -140,8 +149,8 @@ func (tcx TestContext) Tester(f func(tcx TestContext)) { defer tcx.ExpectStdout.Stop() tcx.ExpectStderr = expect.New(tcx.stderr) defer tcx.ExpectStderr.Stop() - withEnv(paths.EnvCLCHome, tcx.homePath, func() { - withEnv(clc.EnvMaxCols, "50", func() { + WithEnv(paths.EnvCLCHome, tcx.homePath, func() { + WithEnv(clc.EnvMaxCols, "50", func() { p := paths.ResolveConfigPath(tcx.ConfigPath) d, _ := filepath.Split(p) check.Must(os.MkdirAll(d, 0700)) @@ -174,48 +183,62 @@ func (tcx TestContext) IO() clc.IO { } } -func (tcx TestContext) AssertStdoutEquals(t *testing.T, text string) { - if !tcx.ExpectStdout.Match(expect.Exact(text), expect.WithTimeout(DefaultTimeout)) { - t.Log("STDOUT:", tcx.ExpectStdout.String()) - t.Fatalf("expect failed, no match for: %s", text) +func (tcx TestContext) AssertStdoutEquals(text string) { + if !tcx.ExpectStdout.Match(expect.Exact(text), expect.WithTimeout(DefaultTimeout()), expect.WithDelay(DefaultDelay)) { + tcx.T.Log("STDOUT:", tcx.ExpectStdout.String()) + tcx.T.Fatalf("expect failed, no match for: %s", text) + } +} + +func (tcx TestContext) AssertStderrEquals(text string) { + if !tcx.ExpectStderr.Match(expect.Exact(text), expect.WithTimeout(DefaultTimeout()), expect.WithDelay(DefaultDelay)) { + tcx.T.Log("STDERR:", tcx.ExpectStderr.String()) + tcx.T.Fatalf("expect failed, no match for: %s", text) } } -func (tcx TestContext) AssertStderrEquals(t *testing.T, text string) { - if !tcx.ExpectStderr.Match(expect.Exact(text), expect.WithTimeout(DefaultTimeout)) { - t.Log("STDERR:", tcx.ExpectStderr.String()) - t.Fatalf("expect failed, no match for: %s", text) +func (tcx TestContext) AssertStderrContains(text string) { + if !tcx.ExpectStderr.Match(expect.Contains(text), expect.WithTimeout(DefaultTimeout()), expect.WithDelay(DefaultDelay)) { + tcx.T.Log("STDERR:", tcx.ExpectStderr.String()) + tcx.T.Fatalf("expect failed, no match for: %s", text) } } -func (tcx TestContext) AssertStdoutContains(t *testing.T, text string) { - if !tcx.ExpectStdout.Match(expect.Contains(text), expect.WithTimeout(DefaultTimeout)) { - t.Log("STDOUT:", tcx.ExpectStdout.String()) - t.Fatalf("expect failed, no match for: %s", text) +func (tcx TestContext) AssertStderrNotContains(text string) { + if tcx.ExpectStderr.Match(expect.Contains(text), expect.WithTimeout(DefaultTimeout()), expect.WithDelay(DefaultDelay)) { + tcx.T.Log("STDERR:", tcx.ExpectStderr.String()) + tcx.T.Fatalf("expect failed, matched: %s", text) } } -func (tcx TestContext) AssertStdoutContainsWithPath(t *testing.T, path string) { +func (tcx TestContext) AssertStdoutContains(text string) { + if !tcx.ExpectStdout.Match(expect.Contains(text), expect.WithTimeout(DefaultTimeout())) { + tcx.T.Log("STDOUT:", tcx.ExpectStdout.String()) + tcx.T.Fatalf("expect failed, no match for: %s", text) + } +} + +func (tcx TestContext) AssertStdoutContainsWithPath(path string) { p := string(check.MustValue(os.ReadFile(path))) - tcx.AssertStdoutContains(t, p) + tcx.AssertStdoutContains(p) } -func (tcx TestContext) AssertStdoutDollar(t *testing.T, text string) { - if !tcx.ExpectStdout.Match(expect.Dollar(text), expect.WithTimeout(DefaultTimeout)) { - t.Log("STDOUT:", tcx.ExpectStdout.String()) - t.Fatalf("expect failed, no match for: %s", text) +func (tcx TestContext) AssertStdoutDollar(text string) { + if !tcx.ExpectStdout.Match(expect.Dollar(text), expect.WithTimeout(DefaultTimeout())) { + tcx.T.Log("STDOUT:", tcx.ExpectStdout.String()) + tcx.T.Fatalf("expect failed, no match for: %s", text) } } -func (tcx TestContext) AssertStdoutDollarWithPath(t *testing.T, path string) { +func (tcx TestContext) AssertStdoutDollarWithPath(path string) { p := string(check.MustValue(os.ReadFile(path))) - tcx.AssertStdoutDollar(t, p) + tcx.AssertStdoutDollar(p) } -func (tcx TestContext) AssertStdoutEqualsWithPath(t *testing.T, path string) { +func (tcx TestContext) AssertStdoutEqualsWithPath(path string) { p := string(check.MustValue(os.ReadFile(path))) p = strings.ReplaceAll(p, "$", "") - tcx.AssertStdoutEquals(t, p) + tcx.AssertStdoutEquals(p) } func (tcx TestContext) WithReset(f func()) { @@ -232,7 +255,16 @@ func (tcx TestContext) CLCExecute(args ...string) { check.Must(tcx.CLC().Execute(a...)) } -func withEnv(name, value string, f func()) { +func (tcx TestContext) WithShell(f func(tcx TestContext)) { + go func() { + tcx.CLCExecute() + }() + // best effort to exit the shell + defer tcx.WriteStdin([]byte("\\exit\n")) + f(tcx) +} + +func WithEnv(name, value string, f func()) { b, ok := os.LookupEnv(name) if ok { // error is ignored @@ -244,3 +276,12 @@ func withEnv(name, value string, f func()) { check.Must(os.Setenv(name, value)) f() } + +func DefaultTimeout() time.Duration { + s := os.Getenv(EnvDefaultTimeout) + d, err := time.ParseDuration(s) + if err != nil { + return 1 * time.Second + } + return d +} diff --git a/internal/it/util.go b/internal/it/util.go index cad3ab2b..b85397db 100644 --- a/internal/it/util.go +++ b/internal/it/util.go @@ -48,15 +48,16 @@ const ( EnvHzVersion = "HZ_VERSION" ) -const ( - DefaultClusterName = "clc-test" -) +func DefaultClusterName() string { + return NewUniqueObjectName("clc-test") +} +var defaultClusterName = DefaultClusterName() var rc *RemoteControllerClientWrapper var rcMu = &sync.RWMutex{} -var defaultTestCluster = NewSingletonTestCluster("default", func() *TestCluster { +var defaultTestCluster = NewSingletonTestCluster(defaultClusterName, func() *TestCluster { port := NextPort() - return rc.startNewCluster(MemberCount(), xmlConfig(DefaultClusterName, port), port) + return rc.startNewCluster(MemberCount(), xmlConfig(defaultClusterName, port), port) }) var idGen = ReferenceIDGenerator{} @@ -69,7 +70,8 @@ func NewUniqueObjectName(service string, labels ...string) string { if ls != "" { ls = fmt.Sprintf("-%s", ls) } - return fmt.Sprintf("test-%s-%d-%d%s", service, idGen.NextID(), rand.Int(), ls) + // make sure the random part is at least 4 characters long + return fmt.Sprintf("test-%s-%d-%d%s", service, idGen.NextID(), rand.Intn(100_000)+1000, ls) } func TraceLoggingEnabled() bool { diff --git a/internal/output/column.go b/internal/output/column.go index 7bbb6c3f..bff803b9 100644 --- a/internal/output/column.go +++ b/internal/output/column.go @@ -1,37 +1,10 @@ package output import ( - "encoding/json" - "fmt" - "math/big" - "reflect" - "strings" - "time" - - "github.com/hazelcast/hazelcast-go-client/serialization" - "github.com/hazelcast/hazelcast-go-client/types" - - "github.com/hazelcast/hazelcast-commandline-client/errors" iserialization "github.com/hazelcast/hazelcast-commandline-client/internal/serialization" ) -type Column struct { - Name string - Type int32 - Value any -} - -func NewStringColumn(value string) Column { - return Column{Type: iserialization.TypeString, Value: value} -} - -func NewNilColumn() Column { - return Column{Type: iserialization.TypeNil} -} - -func NewSkipColumn() Column { - return Column{Type: iserialization.TypeSkip} -} +type Column = iserialization.Column func NewKeyColumn(kt int32, key any) Column { return Column{ @@ -45,7 +18,7 @@ func NewKeyTypeColumn(kt int32) Column { return Column{ Name: NameKeyType, Type: iserialization.TypeString, - Value: iserialization.TypeToString(kt), + Value: iserialization.TypeToLabel(kt), } } @@ -61,243 +34,6 @@ func NewValueTypeColumn(vt int32) Column { return Column{ Name: NameValueType, Type: iserialization.TypeString, - Value: iserialization.TypeToString(vt), - } -} - -func (co Column) SingleLine() (s string) { - if sl, ok := co.Value.(SingleLiner); ok { - return sl.SingleLine() - } - if _, ok := co.Value.(iserialization.NondecodedType); ok { - return ValueNotDecoded - } - switch co.Type { - case iserialization.TypeNil: - s = ValueNil - case iserialization.TypePortable: - s = co.Value.(*iserialization.GenericPortable).String() - case iserialization.TypeDataSerializable: - s = ValueNotDecoded - case iserialization.TypeByte, iserialization.TypeBool, iserialization.TypeUInt16, - iserialization.TypeInt16, iserialization.TypeInt32, iserialization.TypeInt64, - iserialization.TypeFloat32, iserialization.TypeFloat64, iserialization.TypeString, - iserialization.TypeByteArray, iserialization.TypeBoolArray, iserialization.TypeUInt16Array, - iserialization.TypeInt16Array, iserialization.TypeInt32Array, iserialization.TypeInt64Array, - iserialization.TypeFloat32Array, iserialization.TypeFloat64Array, iserialization.TypeStringArray: - s = fmt.Sprintf("%v", co.Value) - case iserialization.TypeUUID: - s = co.Value.(types.UUID).String() - case iserialization.TypeSimpleEntry, iserialization.TypeSimpleImmutableEntry: - s = ValueNotDecoded - case iserialization.TypeJavaClass: - s = co.Value.(string) - case iserialization.TypeJavaDate: - s = co.Value.(time.Time).Format(time.RFC3339) - case iserialization.TypeJavaBigInteger: - s = co.Value.(*big.Int).String() - case iserialization.TypeJavaArray, iserialization.TypeJavaArrayList, iserialization.TypeJavaLinkedList: - s = fmt.Sprintf("%v", co.Value) - case iserialization.TypeJavaDefaultTypeCopyOnWriteArrayList, iserialization.TypeJavaDefaultTypeHashMap, - iserialization.TypeJavaDefaultTypeConcurrentSkipListMap, iserialization.TypeJavaDefaultTypeConcurrentHashMap, - iserialization.TypeJavaDefaultTypeLinkedHashMap, iserialization.TypeJavaDefaultTypeTreeMap, - iserialization.TypeJavaDefaultTypeHashSet, iserialization.TypeJavaDefaultTypeTreeSet, - iserialization.TypeJavaDefaultTypeLinkedHashSet, iserialization.TypeJavaDefaultTypeCopyOnWriteArraySet, - iserialization.TypeJavaDefaultTypeConcurrentSkipListSet, iserialization.TypeJavaDefaultTypeArrayDeque, - iserialization.TypeJavaDefaultTypeLinkedBlockingQueue, iserialization.TypeJavaDefaultTypeArrayBlockingQueue, - iserialization.TypeJavaDefaultTypePriorityBlockingQueue, iserialization.TypeJavaDefaultTypeDelayQueue, - iserialization.TypeJavaDefaultTypeSynchronousQueue, iserialization.TypeJavaDefaultTypeLinkedTransferQueue, - iserialization.TypeJavaDefaultTypePriorityQueue, iserialization.TypeJavaDefaultTypeOptional: - s = ValueNotDecoded - case iserialization.TypeJavaDecimal: - sr, err := iserialization.MarshalDecimal(co.Value) - if err != nil { - s = ValueNotDecoded - } else { - s = sr - } - case iserialization.TypeJavaLocalDate: - sr, err := iserialization.MarshalLocalDate(co.Value) - if err != nil { - s = ValueNotDecoded - } else if sr == nil { - s = ValueNil - } else { - s = *sr - } - case iserialization.TypeJavaLocalTime: - sr, err := iserialization.MarshalLocalTime(co.Value) - if err != nil { - s = ValueNotDecoded - } else if sr == nil { - s = ValueNil - } else { - s = *sr - } - case iserialization.TypeJavaLocalDateTime: - sr, err := iserialization.MarshalLocalDateTime(co.Value) - if err != nil { - s = ValueNotDecoded - } else if sr == nil { - s = ValueNil - } else { - s = *sr - } - case iserialization.TypeJavaOffsetDateTime: - sr, err := iserialization.MarshalOffsetDateTime(co.Value) - if err != nil { - s = ValueNotDecoded - } else if sr == nil { - s = ValueNil - } else { - s = *sr - } - case iserialization.TypeCompact: - sr, err := structToString(co.Value) - if err != nil { - sr = ValueNotDecoded - } - s = sr - case iserialization.TypeCompactWithSchema, iserialization.TypeJavaDefaultTypeSerializable, - iserialization.TypeJavaDefaultTypeExternalizable, iserialization.TypeCsharpCLRSerializationType, - iserialization.TypePythonPickleSerializationType: - s = ValueNotDecoded - case iserialization.TypeJSONSerialization: - sp := strings.Split(string(co.Value.(serialization.JSON)), "\n") - for i, line := range sp { - sp[i] = strings.TrimSpace(line) - } - s = strings.Join(sp, "") - case iserialization.TypeGobSerialization: - s = fmt.Sprintf("%v", co.Value) - case iserialization.TypeHibernate3TypeHibernateCacheKey, iserialization.TypeHibernate3TypeHibernateCacheEntry, - iserialization.TypeHibernate4TypeHibernateCacheKey, iserialization.TypeHibernate4TypeHibernateCacheEntry, - iserialization.TypeHibernate5TypeHibernateCacheKey, iserialization.TypeHibernate5TypeHibernateCacheEntry, - iserialization.TypeHibernate5TypeHibernateNaturalIDKey, - iserialization.TypeJetSerializerFirst, iserialization.TypeJetSerializerLast: - s = ValueNotDecoded - case iserialization.TypeUnknown: - s = ValueUnknown - case iserialization.TypeSkip: - s = ValueSkip - case iserialization.TypeNotDecoded: - s = ValueNotDecoded - default: - s = ValueUnknown - } - idx := strings.Index(s, "\n") - if idx < 0 { - idx = len(s) - } - return s[:idx] -} - -func (co Column) MultiLine() string { - switch co.Type { - case iserialization.TypeNil: - return ValueSkip - case iserialization.TypeUnknown: - return ValueUnknown - } - return fmt.Sprintf("%v", co.Value) -} - -func (co Column) RowExtensions() ([]Column, error) { - switch co.Type { - case iserialization.TypeJSONSerialization: - value := []byte(co.Value.(serialization.JSON)) - var m any - if err := json.Unmarshal(value, &m); err != nil { - return nil, errors.ErrNotDecoded - } - // TODO: nested fields - return jsonValueToColumns(m), nil - case iserialization.TypePortable: - value, ok := co.Value.(*iserialization.GenericPortable) - if !ok { - return nil, errors.ErrNotDecoded - } - cols := make([]Column, len(value.Fields)) - for i, f := range value.Fields { - cols[i] = Column{ - Name: f.Name, - Type: f.Type.ToTypeID(), - Value: f.Value, - } - } - return cols, nil - case iserialization.TypeCompact: - value, err := structToString(co.Value) - if err != nil { - return nil, errors.ErrNotDecoded - } - // the same code path with JSON - var m any - if err := json.Unmarshal([]byte(value), &m); err != nil { - return nil, errors.ErrNotDecoded - } - // TODO: nested fields - return jsonValueToColumns(m), nil - } - return []Column{co}, nil -} - -func jsonValueToColumns(value any) []Column { - if vv, ok := value.(map[string]any); ok { - cols := make([]Column, 0, len(vv)) - for k, v := range vv { - cols = append(cols, jsonValueToColumn(k, v)) - } - return cols - } - return []Column{jsonValueToColumn("", value)} -} - -func jsonValueToColumn(k string, value any) Column { - if _, ok := value.(map[string]any); ok { - // TODO: nested maps are not handled yet - return Column{ - Name: k, - Type: iserialization.TypeNotDecoded, - } - } - return Column{ - Name: k, - Type: jsonValueToTypeID(value), - Value: value, - } -} - -func jsonValueToTypeID(v any) int32 { - switch v.(type) { - case []any: - return iserialization.TypeJavaArray - case string: - return iserialization.TypeString - case float64: - return iserialization.TypeFloat64 - case bool: - return iserialization.TypeBool - case nil: - return iserialization.TypeNil - } - return iserialization.TypeUnknown -} - -func structToString(v any) (string, error) { - vv := reflect.ValueOf(v) - if vv.Kind() == reflect.Pointer { - if vv.IsNil() { - return ValueNil, nil - } - vv = vv.Elem() - } - if vv.Kind() == reflect.Struct { - b, err := json.Marshal(v) - if err != nil { - return "", err - } - return string(b), nil + Value: iserialization.TypeToLabel(vt), } - return fmt.Sprintf("%v", v), nil } diff --git a/internal/output/common.go b/internal/output/common.go index 84d0c394..f393a7d0 100644 --- a/internal/output/common.go +++ b/internal/output/common.go @@ -15,6 +15,6 @@ func convertColumn(col Column) any { case iserialization.TypeSkip: return ValueSkip default: - return col.SingleLine() + return col.Text() } } diff --git a/internal/output/csv_result.go b/internal/output/csv_result.go index 77061c81..8bb3b4e6 100644 --- a/internal/output/csv_result.go +++ b/internal/output/csv_result.go @@ -3,8 +3,8 @@ package output import ( "context" "encoding/csv" - "fmt" "io" + "strings" ) type CSVResult struct { @@ -54,7 +54,7 @@ func makeCSVHeaderFromRow(row Row) []string { func makeCSVRecordFromRow(row Row) []string { r := make([]string, len(row)) for i, c := range row { - r[i] = fmt.Sprint(convertColumn(c)) + r[i] = strings.ReplaceAll(c.Text(), "\n", " ") } return r } diff --git a/internal/output/delimited_result.go b/internal/output/delimited_result.go index e5772a73..7754f1aa 100644 --- a/internal/output/delimited_result.go +++ b/internal/output/delimited_result.go @@ -35,10 +35,10 @@ func (d DelimitedResult) Serialize(ctx context.Context, w io.Writer) (int, error if len(row) == 0 { continue } - sb.WriteString(fmt.Sprintf("%v", d.adapt(row[0]))) + sb.WriteString(d.adapt(row[0])) for _, r := range row[1:] { sb.WriteString(d.delim) - sb.WriteString(fmt.Sprintf("%v", d.adapt(r))) + sb.WriteString(d.adapt(r)) } sb.WriteString("\n") wn, err := w.Write([]byte(sb.String())) @@ -51,8 +51,9 @@ func (d DelimitedResult) Serialize(ctx context.Context, w io.Writer) (int, error } func (d DelimitedResult) adapt(col Column) string { - if d.singleLine { - return col.SingleLine() + text := col.Text() + if !d.singleLine { + return text } - return col.MultiLine() + return strings.ReplaceAll(text, "\n", " ") } diff --git a/internal/output/json_result.go b/internal/output/json_result.go index 7f3bffd4..d6b49239 100644 --- a/internal/output/json_result.go +++ b/internal/output/json_result.go @@ -5,13 +5,6 @@ import ( "encoding/json" "fmt" "io" - "math/big" - "time" - - "github.com/hazelcast/hazelcast-go-client/types" - - "github.com/hazelcast/hazelcast-commandline-client/errors" - iserialization "github.com/hazelcast/hazelcast-commandline-client/internal/serialization" ) type JSONResult struct { @@ -35,7 +28,7 @@ func (jr *JSONResult) Serialize(ctx context.Context, w io.Writer) (int, error) { } m := make(map[string]any, len(row)) for _, col := range row { - v, err := columnToJSONValue(col) + v, err := col.JSONValue() if err != nil { continue } @@ -56,74 +49,3 @@ func (jr *JSONResult) Serialize(ctx context.Context, w io.Writer) (int, error) { n += wn } } - -func columnToJSONValue(col Column) (any, error) { - switch col.Type { - case iserialization.TypeNil: - return nil, nil - case iserialization.TypePortable, iserialization.TypeCompact: - return col.Value, nil - case iserialization.TypeDataSerializable: - return nil, errors.ErrNotDecoded - case iserialization.TypeByte, iserialization.TypeBool, iserialization.TypeUInt16, - iserialization.TypeInt16, iserialization.TypeInt32, iserialization.TypeInt64, - iserialization.TypeFloat32, iserialization.TypeFloat64, iserialization.TypeString, - iserialization.TypeByteArray, iserialization.TypeBoolArray, iserialization.TypeUInt16Array, - iserialization.TypeInt16Array, iserialization.TypeInt32Array, iserialization.TypeInt64Array, - iserialization.TypeFloat32Array, iserialization.TypeFloat64Array, iserialization.TypeStringArray: - return col.Value, nil - case iserialization.TypeUUID: - return col.Value.(types.UUID).String(), nil - case iserialization.TypeSimpleEntry, iserialization.TypeSimpleImmutableEntry: - return nil, errors.ErrNotDecoded - case iserialization.TypeJavaClass: - return col.Value.(string), nil - case iserialization.TypeJavaDate: - return col.Value.(time.Time).Format(time.RFC3339), nil - case iserialization.TypeJavaBigInteger: - return col.Value.(*big.Int).String(), nil - case iserialization.TypeJavaDecimal: - return iserialization.MarshalDecimal(col.Value) - case iserialization.TypeJavaArray, iserialization.TypeJavaArrayList, iserialization.TypeJavaLinkedList: - return col.Value, nil - case iserialization.TypeJavaLocalDate: - sr, err := iserialization.MarshalLocalDate(col.Value) - if err != nil { - return nil, errors.ErrNotDecoded - } else if sr == nil { - return nil, nil - } else { - return *sr, nil - } - case iserialization.TypeJavaLocalTime: - sr, err := iserialization.MarshalLocalTime(col.Value) - if err != nil { - return nil, errors.ErrNotDecoded - } else if sr == nil { - return nil, nil - } else { - return *sr, nil - } - case iserialization.TypeJavaLocalDateTime: - sr, err := iserialization.MarshalLocalDateTime(col.Value) - if err != nil { - return nil, errors.ErrNotDecoded - } else if sr == nil { - return nil, nil - } else { - return *sr, nil - } - case iserialization.TypeJavaOffsetDateTime: - sr, err := iserialization.MarshalOffsetDateTime(col.Value) - if err != nil { - return nil, errors.ErrNotDecoded - } else if sr == nil { - return nil, nil - } else { - return *sr, nil - } - case iserialization.TypeJSONSerialization: - return col.Value, nil - } - return nil, errors.ErrNotDecoded -} diff --git a/internal/output/output.go b/internal/output/output.go index 0072e74b..c28c5277 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -11,14 +11,6 @@ const ( type Type int -type SingleLiner interface { - SingleLine() string -} - -type MultiLiner interface { - MultiLine() []string -} - type RowExtender interface { RowExtensions() []Column } diff --git a/internal/output/pairs.go b/internal/output/pairs.go index 4a11bf18..ac64f984 100644 --- a/internal/output/pairs.go +++ b/internal/output/pairs.go @@ -31,7 +31,7 @@ func ensureTypeValue(ic *hazelcast.ClientInternal, data hazelcast.Data) (int32, t := data.Type() v, err := ic.DecodeData(data) if err != nil { - v = serialization.NondecodedType(serialization.TypeToString(t)) + v = serialization.NondecodedType(serialization.TypeToLabel(t)) } return t, v } diff --git a/internal/output/table_result.go b/internal/output/table_result.go index f726195d..826f43a6 100644 --- a/internal/output/table_result.go +++ b/internal/output/table_result.go @@ -2,7 +2,6 @@ package output import ( "context" - "fmt" "io" "github.com/fatih/color" @@ -60,7 +59,7 @@ func (tr *TableResult) Serialize(ctx context.Context, w io.Writer) (int, error) } row := make([]string, len(vr)) for i, v := range vr { - row[i] = fmt.Sprint(convertColumn(v)) + row[i] = v.Text() } t.WriteRow(row) } diff --git a/internal/output/utils.go b/internal/output/utils.go index 213005fd..89f5d3de 100644 --- a/internal/output/utils.go +++ b/internal/output/utils.go @@ -45,7 +45,7 @@ func MakeTableFromRows(rows []Row) (table.Row, []Row) { } hd.Add(sc.Name) newRow[sc.Name] = sc - newRow[col.Name] = NewSkipColumn() + newRow[col.Name] = Column{Type: serialization.TypeSkip} } continue } @@ -87,7 +87,7 @@ func MakeTableFromRows(rows []Row) (table.Row, []Row) { v.Name = h } row[i] = v - sv := fmt.Sprint(v.Value) + sv := v.Text() if len(sv) > width[i] { width[i] = len(sv) } @@ -132,8 +132,8 @@ func makeTableHeaderFromRow(row Row, maxWidth int) table.Row { func alignmentForType(t int32) int { switch t { - case serialization.TypeByte, serialization.TypeUInt16, serialization.TypeInt16, - serialization.TypeInt32, serialization.TypeInt64, + case serialization.TypeByte, serialization.TypeInt8, serialization.TypeUInt16, + serialization.TypeInt16, serialization.TypeInt32, serialization.TypeInt64, serialization.TypeFloat32, serialization.TypeFloat64, serialization.TypeJavaBigInteger, serialization.TypeJavaDecimal: return -1 // align right diff --git a/internal/serialization/builtin_serialization_it_test.go b/internal/serialization/builtin_serialization_it_test.go new file mode 100644 index 00000000..3c4343fc --- /dev/null +++ b/internal/serialization/builtin_serialization_it_test.go @@ -0,0 +1,111 @@ +package serialization_test + +import ( + "context" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/hazelcast/hazelcast-go-client" + pubserialization "github.com/hazelcast/hazelcast-go-client/serialization" + "github.com/hazelcast/hazelcast-go-client/types" + + _ "github.com/hazelcast/hazelcast-commandline-client/base/commands/map" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" +) + +func TestBuiltinSerialization(t *testing.T) { + tv := time.Date(2023, 2, 3, 4, 5, 6, 7, time.UTC) + testCases := []struct { + name string + value any + delimitedOutput string + jsonOutput string + csvOutput string + }{ + { + name: "byte", + value: byte(8), + delimitedOutput: "8\n", + jsonOutput: `{"this":8}` + "\n", + csvOutput: "this\n8\n", + }, + { + name: "types.Decimal", + value: types.NewDecimal(big.NewInt(100), 10), + delimitedOutput: "1.00E-8\n", + jsonOutput: `{"this":"1.00E-8"}` + "\n", + csvOutput: "this\n1.00E-8\n", + }, + { + name: "string", + value: "test-string", + delimitedOutput: "test-string\n", + jsonOutput: `{"this":"test-string"}` + "\n", + csvOutput: "this\ntest-string\n", + }, + { + name: "types.OffsetDateTime", + value: types.OffsetDateTime(tv), + delimitedOutput: "2023-02-03T04:05:06Z\n", + jsonOutput: `{"this":"2023-02-03T04:05:06Z"}` + "\n", + csvOutput: "this\n2023-02-03T04:05:06Z\n", + }, + { + name: "serialization.JSON", + value: pubserialization.JSON(`{"FieldA":"json-str-1", "FieldB":22}`), + delimitedOutput: `{"FieldA":"json-str-1", "FieldB":22}` + "\n", + jsonOutput: `{"this":{"FieldA":"json-str-1","FieldB":22}}` + "\n", + csvOutput: "this\n" + `"{""FieldA"":""json-str-1"", ""FieldB"":22}"` + "\n", + }, + { + name: "slice", + value: []any{int64(100), "foo", int32(200), true}, + delimitedOutput: "[100, foo, 200, true]\n", + jsonOutput: `{"this":[100,"foo",200,true]}` + "\n", + csvOutput: "this\n" + `"[100, foo, 200, true]"` + "\n", + }, + } + ctx := context.Background() + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + tcx := it.TestContext{T: t} + tcx.Tester(func(tcx it.TestContext) { + it.WithMap(tcx, func(m *hazelcast.Map) { + key := tc.name + check.Must(m.Set(ctx, key, tc.value)) + for _, format := range []string{"delimited", "json", "csv", "table"} { + t.Run(format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "get", "-n", m.Name(), key, "--quite", "-f", format)) + var target string + switch format { + case "delimited": + target = tc.delimitedOutput + case "json": + target = tc.jsonOutput + case "csv": + target = tc.csvOutput + case "table": + target = strings.ToLower(fmt.Sprintf("testdata/builtin_%s.txt", tc.name)) + default: + panic(fmt.Sprintf("unknown format: %s", format)) + } + if format == "table" { + tcx.AssertStdoutDollarWithPath(target) + } else { + tcx.AssertStdoutEquals(target) + } + }) + }) + } + }) + }) + }) + } +} diff --git a/internal/serialization/column.go b/internal/serialization/column.go new file mode 100644 index 00000000..98994341 --- /dev/null +++ b/internal/serialization/column.go @@ -0,0 +1,150 @@ +package serialization + +import ( + "encoding/json" + "sort" + "strings" + + "github.com/hazelcast/hazelcast-go-client/serialization" + + "github.com/hazelcast/hazelcast-commandline-client/errors" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" +) + +type Column struct { + Name string + Type int32 + Value any +} + +func (co Column) Text() (s string) { + if sl, ok := co.Value.(Texter); ok { + return sl.Text() + } + if _, ok := co.Value.(NondecodedType); ok { + return ValueNotDecoded + } + str, ok := ValueToText[co.Type] + if !ok { + return ValueNotDecoded + } + return str(co.Value) +} + +func (co Column) RowExtensions() ([]Column, error) { + switch co.Type { + case TypeJSONSerialization: + value := []byte(co.Value.(serialization.JSON)) + var m any + if err := json.Unmarshal(value, &m); err != nil { + return nil, errors.ErrNotDecoded + } + // TODO: nested fields + return jsonValueToColumns(m), nil + case TypePortable: + value, ok := co.Value.(*GenericPortable) + if !ok { + return nil, errors.ErrNotDecoded + } + cols := make([]Column, len(value.Fields)) + for i, f := range value.Fields { + cols[i] = Column{ + Name: f.Name, + Type: f.Type, + Value: f.Value, + } + } + return cols, nil + case TypeCompact: + if v, ok := co.Value.(ColumnList); ok { + return v, nil + } + return nil, errors.ErrNotDecoded + } + return ColumnList{co}, nil +} + +func (col Column) JSONValue() (any, error) { + if check.IsNil(col.Value) { + return nil, nil + } + if v, ok := col.Value.(JSONValuer); ok { + return v.JSONValue() + } + switch col.Type { + case TypeNil: + return nil, nil + case TypePortable, TypeCompact, + TypeByte, TypeBool, TypeInt8, TypeUInt16, + TypeInt16, TypeInt32, TypeInt64, + TypeFloat32, TypeFloat64, TypeString, + TypeByteArray, TypeBoolArray, TypeInt8Array, TypeUInt16Array, + TypeInt16Array, TypeInt32Array, TypeInt64Array, + TypeFloat32Array, TypeFloat64Array, TypeStringArray, + TypeJavaArray, TypeJavaArrayList, TypeJavaLinkedList, + TypeJSONSerialization: + return col.Value, nil + } + return col.Text(), nil +} + +type ColumnList []Column + +func (cs ColumnList) Text() string { + const delim = "; " + var sb strings.Builder + if len(cs) == 0 { + return "" + } + sb.WriteString(cs[0].Name) + sb.WriteString(":") + sb.WriteString(cs[0].Text()) + for _, c := range cs[1:] { + sb.WriteString(delim) + sb.WriteString(c.Name) + sb.WriteString(":") + sb.WriteString(c.Text()) + } + return sb.String() +} + +func (cs ColumnList) JSONValue() (any, error) { + m := make(map[string]any, len(cs)) + for _, c := range cs { + v, err := c.JSONValue() + if err != nil { + v = ValueNotDecoded + } + m[c.Name] = v + } + return m, nil +} + +func jsonValueToColumns(value any) []Column { + if vv, ok := value.(map[string]any); ok { + cols := make([]Column, 0, len(vv)) + for k, v := range vv { + cols = append(cols, jsonValueToColumn(k, v)) + } + sort.Slice(cols, func(i, j int) bool { + return cols[i].Name < cols[j].Name + }) + return cols + } + return []Column{jsonValueToColumn("", value)} +} + +func jsonValueToColumn(k string, value any) Column { + if _, ok := value.(map[string]any); ok { + // TODO: nested maps are not handled yet + return Column{ + Name: k, + Type: TypeNotDecoded, + } + } + return Column{ + Name: k, + Type: jsonValueToTypeID(value), + Value: value, + } +} diff --git a/internal/serialization/compact_field.go b/internal/serialization/compact_field.go index e202584c..d39914ba 100644 --- a/internal/serialization/compact_field.go +++ b/internal/serialization/compact_field.go @@ -13,14 +13,6 @@ type CompactField struct { Value any } -func readNullableCompactField[T any](field string, f func(string) *T) any { - v := f(field) - if v == nil { - return nil - } - return *v -} - var compactReaders = map[serialization.FieldKind]compactFieldReader{ serialization.FieldKindBoolean: func(r serialization.CompactReader, field string) any { return r.ReadBoolean(field) @@ -67,37 +59,37 @@ var compactReaders = map[serialization.FieldKind]compactFieldReader{ return r.ReadArrayOfFloat64(field) }, serialization.FieldKindString: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadString) + return r.ReadString(field) }, serialization.FieldKindArrayOfString: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfString(field) }, serialization.FieldKindDecimal: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadDecimal) + return r.ReadDecimal(field) }, serialization.FieldKindArrayOfDecimal: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfDecimal(field) }, serialization.FieldKindTime: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadTime) + return r.ReadTime(field) }, serialization.FieldKindArrayOfTime: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfTime(field) }, serialization.FieldKindDate: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadDate) + return r.ReadDate(field) }, serialization.FieldKindArrayOfDate: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfDate(field) }, serialization.FieldKindTimestamp: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadTimestamp) + return r.ReadTimestamp(field) }, serialization.FieldKindArrayOfTimestamp: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfTimestamp(field) }, serialization.FieldKindTimestampWithTimezone: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadTimestampWithTimezone) + return r.ReadTimestampWithTimezone(field) }, serialization.FieldKindArrayOfTimestampWithTimezone: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfTimestampWithTimezone(field) @@ -111,43 +103,43 @@ var compactReaders = map[serialization.FieldKind]compactFieldReader{ // FieldKindPortable : Not decoded due to spec. // FieldKindArrayOfPortable : Not decoded due to spec. serialization.FieldKindNullableBoolean: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableBoolean) + return r.ReadNullableBoolean(field) }, serialization.FieldKindArrayOfNullableBoolean: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableBoolean(field) }, serialization.FieldKindNullableInt8: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableInt8) + return r.ReadNullableInt8(field) }, serialization.FieldKindArrayOfNullableInt8: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableInt8(field) }, serialization.FieldKindNullableInt16: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableInt16) + return r.ReadNullableInt16(field) }, serialization.FieldKindArrayOfNullableInt16: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableInt16(field) }, serialization.FieldKindNullableInt32: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableInt32) + return r.ReadNullableInt32(field) }, serialization.FieldKindArrayOfNullableInt32: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableInt32(field) }, serialization.FieldKindNullableInt64: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableInt64) + return r.ReadNullableInt64(field) }, serialization.FieldKindArrayOfNullableInt64: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableInt64(field) }, serialization.FieldKindNullableFloat32: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableFloat32) + return r.ReadNullableFloat32(field) }, serialization.FieldKindArrayOfNullableFloat32: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableFloat32(field) }, serialization.FieldKindNullableFloat64: func(r serialization.CompactReader, field string) any { - return readNullableCompactField(field, r.ReadNullableFloat64) + return r.ReadNullableFloat64(field) }, serialization.FieldKindArrayOfNullableFloat64: func(r serialization.CompactReader, field string) any { return r.ReadArrayOfNullableFloat64(field) diff --git a/internal/serialization/compact_serialization_it_test.go b/internal/serialization/compact_serialization_it_test.go new file mode 100644 index 00000000..205584b3 --- /dev/null +++ b/internal/serialization/compact_serialization_it_test.go @@ -0,0 +1,369 @@ +package serialization_test + +import ( + "context" + "math" + "math/big" + "reflect" + "testing" + "time" + + "github.com/hazelcast/hazelcast-go-client" + "github.com/hazelcast/hazelcast-go-client/serialization" + "github.com/hazelcast/hazelcast-go-client/types" + + _ "github.com/hazelcast/hazelcast-commandline-client/base/commands/map" + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" +) + +func TestCompactSerialization(t *testing.T) { + testCases := []struct { + name string + f func(t *testing.T) + }{ + {name: "CompactOthers", f: compactOthersTest}, + {name: "CompactPrimitiveArrays", f: compactPrimitiveArraysTest}, + {name: "CompactPrimitives", f: compactPrimitivesTest}, + } + for _, tc := range testCases { + t.Run(tc.name, tc.f) + } +} + +func compactPrimitiveArraysTest(t *testing.T) { + tcx := it.TestContext{ + T: t, + ConfigCallback: func(tcx it.TestContext) { + tcx.ClientConfig.Serialization.Compact.SetSerializers(primitiveArraysSerializer{}) + }, + } + tcx.Tester(func(tcx it.TestContext) { + b := true + i8 := int8(8) + value := primitiveArrays{ + fullBoolArray: []bool{true, false}, + fullInt8Array: []int8{math.MinInt8, 0, math.MaxInt8}, + fullInt16Array: []int16{math.MinInt16, 0, math.MaxInt16}, + fullInt32Array: []int32{math.MinInt32, 0, math.MaxInt32}, + fullInt64Array: []int64{math.MinInt64, 0, math.MinInt64}, + fullNullableBoolArray: []*bool{&b, nil}, + fullNullableInt8Array: []*int8{nil, &i8}, + } + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "value", value)) + testCases := []struct { + format string + target string + }{ + { + format: "delimited", + target: "emptyBoolArray:[]; emptyFloat32Array:[]; emptyFloat64Array:[]; emptyInt16Array:[]; emptyInt32Array:[]; emptyInt64Array:[]; emptyInt8Array:[]; emptyNullableBoolArray:[]; emptyNullableInt8Array:[]; fullBoolArray:[true, false]; fullFloat32Array:[]; fullFloat64Array:[]; fullInt16Array:[-32768, 0, 32767]; fullInt32Array:[-2147483648, 0, 2147483647]; fullInt64Array:[-9223372036854775808, 0, -9223372036854775808]; fullInt8Array:[-128, 0, 127]; fullNullableBoolArray:[true, -]; fullNullableInt8Array:[-, 8]\n", + }, + { + format: "json", + target: `{"this":{"emptyBoolArray":null,"emptyFloat32Array":null,"emptyFloat64Array":null,"emptyInt16Array":null,"emptyInt32Array":null,"emptyInt64Array":null,"emptyInt8Array":null,"emptyNullableBoolArray":null,"emptyNullableInt8Array":null,"fullBoolArray":[true,false],"fullFloat32Array":null,"fullFloat64Array":null,"fullInt16Array":[-32768,0,32767],"fullInt32Array":[-2147483648,0,2147483647],"fullInt64Array":[-9223372036854775808,0,-9223372036854775808],"fullInt8Array":[-128,0,127],"fullNullableBoolArray":[true,null],"fullNullableInt8Array":[null,8]}}` + "\n", + }, + { + format: "csv", + target: "this\n" + `"emptyBoolArray:[]; emptyFloat32Array:[]; emptyFloat64Array:[]; emptyInt16Array:[]; emptyInt32Array:[]; emptyInt64Array:[]; emptyInt8Array:[]; emptyNullableBoolArray:[]; emptyNullableInt8Array:[]; fullBoolArray:[true, false]; fullFloat32Array:[]; fullFloat64Array:[]; fullInt16Array:[-32768, 0, 32767]; fullInt32Array:[-2147483648, 0, 2147483647]; fullInt64Array:[-9223372036854775808, 0, -9223372036854775808]; fullInt8Array:[-128, 0, 127]; fullNullableBoolArray:[true, -]; fullNullableInt8Array:[-, 8]"` + "\n", + }, + + { + format: "table", + target: "testdata/primitive_arrays_table.txt", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "value", "--quite", "-f", tc.format)) + if tc.format == "table" { + tcx.AssertStdoutDollarWithPath(tc.target) + } else { + tcx.AssertStdoutEquals(tc.target) + } + }) + }) + } + }) + + }) +} + +func compactPrimitivesTest(t *testing.T) { + tcx := it.TestContext{ + T: t, + ConfigCallback: func(tcx it.TestContext) { + tcx.ClientConfig.Serialization.Compact.SetSerializers(primitivesSerializer{}) + }, + } + tcx.Tester(func(tcx it.TestContext) { + i8 := int8(8) + b := false + value := primitives{ + valueInt8: i8, + nullInt8NotNull: &i8, + valueBool: true, + nullBoolNotNull: &b, + } + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "value", value)) + testCases := []struct { + format string + target string + }{ + { + format: "delimited", + target: "nullBoolNotNull:false; nullBoolNull:-; nullInt8NotNull:8; nullInt8Null:-; valueBool:true; valueInt8:8\n", + }, + { + format: "json", + target: `{"this":{"nullBoolNotNull":false,"nullBoolNull":null,"nullInt8NotNull":8,"nullInt8Null":null,"valueBool":true,"valueInt8":8}}` + "\n", + }, + { + format: "csv", + target: "this\nnullBoolNotNull:false; nullBoolNull:-; nullInt8NotNull:8; nullInt8Null:-; valueBool:true; valueInt8:8\n", + }, + { + format: "table", + target: "testdata/compact_primitives_table.txt", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "value", "--quite", "-f", tc.format)) + if tc.format == "table" { + tcx.AssertStdoutDollarWithPath(tc.target) + } else { + tcx.AssertStdoutEquals(tc.target) + } + }) + }) + } + }) + + }) +} + +func compactOthersTest(t *testing.T) { + tcx := it.TestContext{ + T: t, + ConfigCallback: func(tcx it.TestContext) { + tcx.ClientConfig.Serialization.Compact.SetSerializers(othersSerializer{}) + }, + } + tcx.Tester(func(tcx it.TestContext) { + s := "foobar" + dtz := time.Date(2023, 4, 5, 12, 33, 45, 46, time.UTC) + dc := types.NewDecimal(big.NewInt(1234), 56) + value := others{ + nullStringNotNull: &s, + offsetDateTimeNotNull: (*types.OffsetDateTime)(&dtz), + decimalNotNull: &dc, + } + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "value", value)) + testCases := []struct { + format string + target string + }{ + { + format: "delimited", + target: "decimalNotNull:1.234E-53; decimalNull:-; nullStringNotNull:foobar; nullStringNull:-; offsetDateTimeNotNull:2023-04-05T12:33:45Z; offsetDateTimeNull:-\n", + }, + { + format: "json", + target: `{"this":{"decimalNotNull":"1.234E-53","decimalNull":null,"nullStringNotNull":"foobar","nullStringNull":null,"offsetDateTimeNotNull":"2023-04-05T12:33:45Z","offsetDateTimeNull":null}}` + "\n", + }, + { + format: "csv", + target: "this\n" + `decimalNotNull:1.234E-53; decimalNull:-; nullStringNotNull:foobar; nullStringNull:-; offsetDateTimeNotNull:2023-04-05T12:33:45Z; offsetDateTimeNull:-` + "\n", + }, + { + format: "table", + target: "testdata/compact_others_table.txt", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "value", "--quite", "-f", tc.format)) + if tc.format == "table" { + tcx.AssertStdoutDollarWithPath(tc.target) + } else { + tcx.AssertStdoutEquals(tc.target) + } + }) + }) + } + }) + + }) +} + +type primitives struct { + valueInt8 int8 + nullInt8Null *int8 + nullInt8NotNull *int8 + valueBool bool + nullBoolNull *bool + nullBoolNotNull *bool +} + +type primitivesSerializer struct{} + +func (s primitivesSerializer) Type() reflect.Type { + return reflect.TypeOf(primitives{}) +} + +func (s primitivesSerializer) TypeName() string { + return "primitives" +} + +func (s primitivesSerializer) Read(r serialization.CompactReader) interface{} { + return primitives{ + valueInt8: r.ReadInt8("valueInt8"), + nullInt8Null: r.ReadNullableInt8("nullInt8Null"), + nullInt8NotNull: r.ReadNullableInt8("nullInt8NotNull"), + valueBool: r.ReadBoolean("valueBool"), + nullBoolNull: r.ReadNullableBoolean("nullBoolNull"), + nullBoolNotNull: r.ReadNullableBoolean("nullBoolNotNull"), + } +} + +func (s primitivesSerializer) Write(w serialization.CompactWriter, v interface{}) { + vv := v.(primitives) + w.WriteInt8("valueInt8", vv.valueInt8) + w.WriteNullableInt8("nullInt8Null", vv.nullInt8Null) + w.WriteNullableInt8("nullInt8NotNull", vv.nullInt8NotNull) + w.WriteBoolean("valueBool", vv.valueBool) + w.WriteNullableBoolean("nullBoolNull", vv.nullBoolNull) + w.WriteNullableBoolean("nullBoolNotNull", vv.nullBoolNotNull) +} + +type primitiveArrays struct { + emptyBoolArray []bool + fullBoolArray []bool + emptyInt8Array []int8 + fullInt8Array []int8 + emptyInt16Array []int16 + fullInt16Array []int16 + emptyInt32Array []int32 + fullInt32Array []int32 + emptyInt64Array []int64 + fullInt64Array []int64 + emptyFloat32Array []float32 + fullFloat32Array []float32 + emptyFloat64Array []float64 + fullFloat64Array []float64 + emptyNullableBoolArray []*bool + fullNullableBoolArray []*bool + emptyNullableInt8Array []*int8 + fullNullableInt8Array []*int8 +} + +type primitiveArraysSerializer struct{} + +func (s primitiveArraysSerializer) Type() reflect.Type { + return reflect.TypeOf(primitiveArrays{}) +} + +func (s primitiveArraysSerializer) TypeName() string { + return "primitiveArrays" +} + +func (s primitiveArraysSerializer) Read(r serialization.CompactReader) interface{} { + return primitiveArrays{ + emptyBoolArray: r.ReadArrayOfBoolean("emptyBoolArray"), + fullBoolArray: r.ReadArrayOfBoolean("fullBoolArray"), + emptyInt8Array: r.ReadArrayOfInt8("emptyInt8Array"), + fullInt8Array: r.ReadArrayOfInt8("fullInt8Array"), + emptyInt16Array: r.ReadArrayOfInt16("emptyInt16Array"), + fullInt16Array: r.ReadArrayOfInt16("fullInt16Array"), + emptyInt32Array: r.ReadArrayOfInt32("emptyInt32Array"), + fullInt32Array: r.ReadArrayOfInt32("fullInt32Array"), + emptyInt64Array: r.ReadArrayOfInt64("emptyInt64Array"), + fullInt64Array: r.ReadArrayOfInt64("fullInt64Array"), + emptyFloat32Array: r.ReadArrayOfFloat32("emptyFloat32Array"), + fullFloat32Array: r.ReadArrayOfFloat32("fullFloat32Array"), + emptyFloat64Array: r.ReadArrayOfFloat64("emptyFloat64Array"), + fullFloat64Array: r.ReadArrayOfFloat64("fullFloat64Array"), + emptyNullableBoolArray: r.ReadArrayOfNullableBoolean("emptyNullableBoolArray"), + fullNullableBoolArray: r.ReadArrayOfNullableBoolean("fullNullableBoolArray"), + emptyNullableInt8Array: r.ReadArrayOfNullableInt8("emptyNullableInt8Array"), + fullNullableInt8Array: r.ReadArrayOfNullableInt8("fullNullableInt8Array"), + } +} + +func (s primitiveArraysSerializer) Write(w serialization.CompactWriter, v interface{}) { + vv := v.(primitiveArrays) + w.WriteArrayOfBoolean("emptyBoolArray", vv.emptyBoolArray) + w.WriteArrayOfBoolean("fullBoolArray", vv.fullBoolArray) + w.WriteArrayOfInt8("emptyInt8Array", vv.emptyInt8Array) + w.WriteArrayOfInt8("fullInt8Array", vv.fullInt8Array) + w.WriteArrayOfInt16("emptyInt16Array", vv.emptyInt16Array) + w.WriteArrayOfInt16("fullInt16Array", vv.fullInt16Array) + w.WriteArrayOfInt32("emptyInt32Array", vv.emptyInt32Array) + w.WriteArrayOfInt32("fullInt32Array", vv.fullInt32Array) + w.WriteArrayOfInt64("emptyInt64Array", vv.emptyInt64Array) + w.WriteArrayOfInt64("fullInt64Array", vv.fullInt64Array) + w.WriteArrayOfFloat32("emptyFloat32Array", vv.emptyFloat32Array) + w.WriteArrayOfFloat32("fullFloat32Array", vv.fullFloat32Array) + w.WriteArrayOfFloat64("emptyFloat64Array", vv.emptyFloat64Array) + w.WriteArrayOfFloat64("fullFloat64Array", vv.fullFloat64Array) + w.WriteArrayOfNullableBoolean("fullNullableBoolArray", vv.fullNullableBoolArray) + w.WriteArrayOfNullableBoolean("emptyNullableBoolArray", vv.emptyNullableBoolArray) + w.WriteArrayOfNullableInt8("emptyNullableInt8Array", vv.emptyNullableInt8Array) + w.WriteArrayOfNullableInt8("fullNullableInt8Array", vv.fullNullableInt8Array) +} + +type others struct { + nullStringNull *string + nullStringNotNull *string + offsetDateTimeNull *types.OffsetDateTime + offsetDateTimeNotNull *types.OffsetDateTime + decimalNull *types.Decimal + decimalNotNull *types.Decimal +} + +type othersSerializer struct{} + +func (s othersSerializer) Type() reflect.Type { + return reflect.TypeOf(others{}) +} + +func (s othersSerializer) TypeName() string { + return "others" +} + +func (s othersSerializer) Read(r serialization.CompactReader) interface{} { + return others{ + nullStringNull: r.ReadString("nullStringNull"), + nullStringNotNull: r.ReadString("nullStringNotNull"), + offsetDateTimeNull: r.ReadTimestampWithTimezone("offsetDateTimeNull"), + offsetDateTimeNotNull: r.ReadTimestampWithTimezone("offsetDateTimeNotNull"), + decimalNull: r.ReadDecimal("decimalNull"), + decimalNotNull: r.ReadDecimal("decimalNotNull"), + } +} + +func (s othersSerializer) Write(w serialization.CompactWriter, v interface{}) { + vv := v.(others) + w.WriteString("nullStringNull", vv.nullStringNull) + w.WriteString("nullStringNotNull", vv.nullStringNotNull) + w.WriteTimestampWithTimezone("offsetDateTimeNull", vv.offsetDateTimeNull) + w.WriteTimestampWithTimezone("offsetDateTimeNotNull", vv.offsetDateTimeNotNull) + w.WriteDecimal("decimalNull", vv.decimalNull) + w.WriteDecimal("decimalNotNull", vv.decimalNotNull) +} diff --git a/internal/serialization/generic_compact.go b/internal/serialization/generic_compact.go index d1af663f..b12970e6 100644 --- a/internal/serialization/generic_compact.go +++ b/internal/serialization/generic_compact.go @@ -1,15 +1,10 @@ package serialization import ( - "fmt" - "math/big" "reflect" - "sync" - "time" "github.com/hazelcast/hazelcast-go-client" "github.com/hazelcast/hazelcast-go-client/serialization" - "github.com/hazelcast/hazelcast-go-client/types" ) type compactFieldReader func(r serialization.CompactReader, field string) any @@ -19,109 +14,74 @@ type SchemaInfo struct { TypeName string } -type GenericCompactDeserializer struct { - SchemaInfos *sync.Map -} - -func NewGenericCompactDeserializer() *GenericCompactDeserializer { - return &GenericCompactDeserializer{ - SchemaInfos: &sync.Map{}, - } -} +type GenericCompactDeserializer struct{} -func (cm *GenericCompactDeserializer) Read(schema *hazelcast.Schema, reader serialization.CompactReader) interface{} { - v := reflect.New(cm.makeType(schema)) - for i, fd := range schema.FieldDefinitions() { +func (cm GenericCompactDeserializer) Read(schema *hazelcast.Schema, reader serialization.CompactReader) interface{} { + fds := schema.FieldDefinitions() + cs := make(ColumnList, len(fds)) + for i, fd := range fds { r := compactReaders[fd.Kind] - value := reflect.ValueOf(r(reader, fd.Name)) - if value.Interface() == nil { - continue + v := r(reader, fd.Name) + c := Column{ + Name: fd.Name, + Value: v, } - v.Elem().Field(i).Set(value) - } - return v.Interface() -} - -func (cm *GenericCompactDeserializer) makeType(schema *hazelcast.Schema) reflect.Type { - var t reflect.Type - vi, ok := cm.SchemaInfos.Load(schema.ID()) - if ok { - v := vi.(SchemaInfo) - t = v.Type - } else { - fds := schema.FieldDefinitions() - fs := make([]reflect.StructField, len(fds)) - for i, f := range fds { - fs[i] = reflect.StructField{ - Name: fmt.Sprintf("Field%03d", i), - Type: fieldKindToType[f.Kind], - Tag: reflect.StructTag(fmt.Sprintf("json:\"%s\"", f.Name)), - } + if v == nil { + c.Type = TypeNil + } else { + c.Type = fieldKindToType[fd.Kind] } - t = reflect.StructOf(fs) - cm.SchemaInfos.Store(schema.ID(), SchemaInfo{ - Type: t, - TypeName: schema.TypeName, - }) + cs[i] = c } - return t + return cs } -var fieldKindToType map[serialization.FieldKind]reflect.Type +var fieldKindToType map[serialization.FieldKind]int32 func init() { - var a any - var b bool - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var f32 float32 - var f64 float64 - var t time.Time - fieldKindToType = map[serialization.FieldKind]reflect.Type{ - serialization.FieldKindNotAvailable: nil, - serialization.FieldKindBoolean: reflect.TypeOf(b), - serialization.FieldKindArrayOfBoolean: reflect.TypeOf([]bool{}), - serialization.FieldKindInt8: reflect.TypeOf(i8), - serialization.FieldKindArrayOfInt8: reflect.TypeOf([]int8{}), - serialization.FieldKindInt16: reflect.TypeOf(i16), - serialization.FieldKindArrayOfInt16: reflect.TypeOf([]int16{}), - serialization.FieldKindInt32: reflect.TypeOf(i32), - serialization.FieldKindArrayOfInt32: reflect.TypeOf([]int32{}), - serialization.FieldKindInt64: reflect.TypeOf(i64), - serialization.FieldKindArrayOfInt64: reflect.TypeOf([]int64{}), - serialization.FieldKindFloat32: reflect.TypeOf(f32), - serialization.FieldKindArrayOfFloat32: reflect.TypeOf([]float32{}), - serialization.FieldKindFloat64: reflect.TypeOf(f64), - serialization.FieldKindArrayOfFloat64: reflect.TypeOf([]float64{}), - serialization.FieldKindString: reflect.TypeOf(""), - serialization.FieldKindArrayOfString: reflect.TypeOf([]string{}), - serialization.FieldKindDecimal: reflect.TypeOf(types.NewDecimal(new(big.Int), 0)), - serialization.FieldKindArrayOfDecimal: reflect.TypeOf([]types.Decimal{}), - serialization.FieldKindTime: reflect.TypeOf(types.LocalTime(t)), - serialization.FieldKindArrayOfTime: reflect.TypeOf([]types.LocalTime{}), - serialization.FieldKindDate: reflect.TypeOf(types.LocalDate(t)), - serialization.FieldKindArrayOfDate: reflect.TypeOf([]types.LocalDate{}), - serialization.FieldKindTimestamp: reflect.TypeOf(types.LocalDateTime(t)), - serialization.FieldKindArrayOfTimestamp: reflect.TypeOf([]types.LocalDateTime{}), - serialization.FieldKindTimestampWithTimezone: reflect.TypeOf(types.OffsetDateTime(t)), - serialization.FieldKindArrayOfTimestampWithTimezone: reflect.TypeOf([]types.OffsetDateTime{}), - serialization.FieldKindCompact: reflect.TypeOf(a), - serialization.FieldKindArrayOfCompact: reflect.TypeOf([]any{}), - serialization.FieldKindNullableBoolean: reflect.TypeOf(&b), - serialization.FieldKindArrayOfNullableBoolean: reflect.TypeOf([]*bool{}), - serialization.FieldKindNullableInt8: reflect.TypeOf(&i8), - serialization.FieldKindArrayOfNullableInt8: reflect.TypeOf([]*int8{}), - serialization.FieldKindNullableInt16: reflect.TypeOf(&i16), - serialization.FieldKindArrayOfNullableInt16: reflect.TypeOf([]*int16{}), - serialization.FieldKindNullableInt32: reflect.TypeOf(&i32), - serialization.FieldKindArrayOfNullableInt32: reflect.TypeOf([]*int32{}), - serialization.FieldKindNullableInt64: reflect.TypeOf(&i64), - serialization.FieldKindArrayOfNullableInt64: reflect.TypeOf([]*int64{}), - serialization.FieldKindNullableFloat32: reflect.TypeOf(&f32), - serialization.FieldKindArrayOfNullableFloat32: reflect.TypeOf([]*float32{}), - serialization.FieldKindNullableFloat64: reflect.TypeOf(&f64), - serialization.FieldKindArrayOfNullableFloat64: reflect.TypeOf([]*float64{}), + fieldKindToType = map[serialization.FieldKind]int32{ + serialization.FieldKindNotAvailable: TypeNil, + serialization.FieldKindBoolean: TypeBool, + serialization.FieldKindArrayOfBoolean: TypeBoolArray, + serialization.FieldKindInt8: TypeInt8, + serialization.FieldKindArrayOfInt8: TypeInt8Array, + serialization.FieldKindInt16: TypeInt16, + serialization.FieldKindArrayOfInt16: TypeInt16Array, + serialization.FieldKindInt32: TypeInt32, + serialization.FieldKindArrayOfInt32: TypeInt32Array, + serialization.FieldKindInt64: TypeInt64, + serialization.FieldKindArrayOfInt64: TypeInt64Array, + serialization.FieldKindFloat32: TypeFloat32, + serialization.FieldKindArrayOfFloat32: TypeFloat32Array, + serialization.FieldKindFloat64: TypeFloat64, + serialization.FieldKindArrayOfFloat64: TypeFloat64Array, + serialization.FieldKindString: TypeString, + serialization.FieldKindArrayOfString: TypeStringArray, + serialization.FieldKindDecimal: TypeJavaDecimal, + serialization.FieldKindArrayOfDecimal: TypeDecimalArray, + serialization.FieldKindTime: TypeJavaLocalTime, + serialization.FieldKindArrayOfTime: TypeJavaLocalTimeArray, + serialization.FieldKindDate: TypeJavaLocalDate, + serialization.FieldKindArrayOfDate: TypeJavaLocalDateArray, + serialization.FieldKindTimestamp: TypeJavaLocalDateTime, + serialization.FieldKindArrayOfTimestamp: TypeJavaLocalDateTimeArray, + serialization.FieldKindTimestampWithTimezone: TypeJavaOffsetDateTime, + serialization.FieldKindArrayOfTimestampWithTimezone: TypeJavaOffsetDateTimeArray, + serialization.FieldKindCompact: TypeCompact, + serialization.FieldKindArrayOfCompact: TypeCompactArray, + serialization.FieldKindNullableBoolean: TypeBool, + serialization.FieldKindArrayOfNullableBoolean: TypeBoolArray, + serialization.FieldKindNullableInt8: TypeInt8, + serialization.FieldKindArrayOfNullableInt8: TypeInt8Array, + serialization.FieldKindNullableInt16: TypeInt16, + serialization.FieldKindArrayOfNullableInt16: TypeInt16Array, + serialization.FieldKindNullableInt32: TypeInt32, + serialization.FieldKindArrayOfNullableInt32: TypeInt32Array, + serialization.FieldKindNullableInt64: TypeInt64, + serialization.FieldKindArrayOfNullableInt64: TypeInt64Array, + serialization.FieldKindNullableFloat32: TypeFloat32, + serialization.FieldKindArrayOfNullableFloat32: TypeFloat32Array, + serialization.FieldKindNullableFloat64: TypeFloat64, + serialization.FieldKindArrayOfNullableFloat64: TypeFloat64Array, } } diff --git a/internal/serialization/generic_portable.go b/internal/serialization/generic_portable.go index b0f0a047..62485cad 100644 --- a/internal/serialization/generic_portable.go +++ b/internal/serialization/generic_portable.go @@ -1,18 +1,15 @@ package serialization import ( - "encoding/json" - "strings" + "sort" "github.com/hazelcast/hazelcast-go-client/serialization" ) type portableFieldReader func(r serialization.PortableReader, field string) any -type portableFieldWriter func(w serialization.PortableWriter, field string, value any) - type GenericPortable struct { - Fields []PortableField + Fields ColumnList FID int32 CID int32 } @@ -25,7 +22,7 @@ func (p *GenericPortable) ClassID() int32 { return p.CID } -func (p *GenericPortable) WritePortable(writer serialization.PortableWriter) { +func (p *GenericPortable) WritePortable(w serialization.PortableWriter) { panic("serialization.GenericPortable.WritePortable is not supposed to be called") } @@ -33,61 +30,12 @@ func (g *GenericPortable) ReadPortable(r serialization.PortableReader) { panic("serialization.GenericPortable.ReadPortable is not supposed to be called") } -func (g *GenericPortable) String() string { - fs := g.Fields - if len(fs) == 0 { - return "" - } - sb := strings.Builder{} - sb.WriteString(fs[0].String()) - for _, f := range fs[1:] { - // middle dot - sb.WriteString("\u00b7") - sb.WriteString(f.String()) - } - return sb.String() +func (g *GenericPortable) Text() string { + return g.Fields.Text() } -func (g *GenericPortable) MarshalJSON() ([]byte, error) { - m := make(map[string]any, len(g.Fields)) - for _, f := range g.Fields { - m[f.Name] = g.marshalField(f) - } - return json.Marshal(m) -} - -func (g *GenericPortable) marshalField(f PortableField) any { - switch f.Type { - case PortableTypeTime: - // ignoring the error - sr, _ := MarshalLocalTime(f.Value) - if sr == nil { - return nil - } - return *sr - case PortableTypeDate: - // ignoring the error - sr, _ := MarshalLocalDateTime(f.Value) - if sr == nil { - return nil - } - return *sr - case PortableTypeTimestamp: - // ignoring the error - sr, _ := MarshalLocalDateTime(f.Value) - if sr == nil { - return nil - } - return *sr - case PortableTypeTimestampWithTimezone: - // ignoring the error - sr, _ := MarshalOffsetDateTime(f.Value) - if sr == nil { - return nil - } - return *sr - } - return f.Value +func (g *GenericPortable) JSONValue() (any, error) { + return g.Fields.JSONValue() } type GenericPortableSerializer struct{} @@ -104,12 +52,16 @@ func (gs GenericPortableSerializer) CreatePortableValue(factoryID, classID int32 } func (gs GenericPortableSerializer) ReadPortableWithClassDefinition(portable serialization.Portable, cd *serialization.ClassDefinition, reader serialization.PortableReader) { + v := portable.(*GenericPortable) for name, f := range cd.Fields { - v := portable.(*GenericPortable) - v.Fields = append(v.Fields, PortableField{ + v.Fields = append(v.Fields, Column{ Name: f.Name, - Type: PortableFieldType(f.Type + 1), + Type: FieldDefinitionIDToType[f.Type], Value: portableReaders[f.Type](reader, name), }) } + // sort fields + sort.Slice(v.Fields, func(i, j int) bool { + return v.Fields[i].Name < v.Fields[j].Name + }) } diff --git a/internal/serialization/marshal.go b/internal/serialization/marshal.go index de1c6e35..77e218dc 100644 --- a/internal/serialization/marshal.go +++ b/internal/serialization/marshal.go @@ -83,12 +83,12 @@ func MarshalOffsetDateTime(v any) (*string, error) { func MarshalDecimal(v any) (string, error) { switch vv := v.(type) { case types.Decimal: - return fmt.Sprintf("%s ^%d", vv.UnscaledValue().String(), vv.Scale()), nil + return fmt.Sprintf("%s/^%d", vv.UnscaledValue().String(), vv.Scale()), nil case *types.Decimal: if vv == (*types.Decimal)(nil) { return ValueNil, nil } - return fmt.Sprintf("%s ^%d", vv.UnscaledValue().String(), vv.Scale()), nil + return fmt.Sprintf("%s/^%d", vv.UnscaledValue().String(), vv.Scale()), nil default: return "", errors.ErrNotDecoded } diff --git a/internal/serialization/portable_field.go b/internal/serialization/portable_field.go index 36ccca16..1be60be3 100644 --- a/internal/serialization/portable_field.go +++ b/internal/serialization/portable_field.go @@ -1,264 +1,40 @@ package serialization import ( - "fmt" - "strings" - "github.com/hazelcast/hazelcast-go-client/serialization" ) -type PortableField struct { - Name string - Type PortableFieldType - Value any -} - -func (f PortableField) String() string { - return fmt.Sprintf("%s:%s", f.Name, f.formatValue()) -} - -func (f PortableField) formatValue() (v string) { - defer func() { - if e := recover(); e != nil { - v = ValueNotDecoded - } - }() - if f.Value == nil { - return "" - } - switch f.Type { - case PortableTypeNone: - case PortableTypePortable: - // nested portable is not supported yet - return ValueNotDecoded - case PortableTypeByte, PortableTypeBool, PortableTypeUint16, - PortableTypeInt16, PortableTypeInt32, PortableTypeInt64, - PortableTypeFloat32, PortableTypeFloat64, PortableTypeString, - PortableTypePortableArray, PortableTypeByteArray, PortableTypeBoolArray, - PortableTypeUInt16Array, PortableTypeInt16Array, PortableTypeInt32Array, - PortableTypeInt64Array, PortableTypeFloat32Array, PortableTypeFloat64Array, - PortableTypeStringArray: - return fmt.Sprintf("%v", f.Value) - case PortableTypeDecimal: - sr, err := MarshalDecimal(f.Value) - if err != nil { - return ValueNotDecoded - } - return sr - case PortableTypeDecimalArray: - return ValueNotDecoded - case PortableTypeTime: - sr, err := MarshalLocalTime(f.Value) - if err != nil { - return ValueNotDecoded - } else if sr == nil { - return ValueNil - } else { - return *sr - } - case PortableTypeTimeArray: - return ValueNotDecoded - case PortableTypeDate: - sr, err := MarshalLocalDate(f.Value) - if err != nil { - return ValueNotDecoded - } else if sr == nil { - return ValueNil - } else { - return *sr - } - case PortableTypeDateArray: - return ValueNotDecoded - case PortableTypeTimestamp: - sr, err := MarshalLocalDateTime(f.Value) - if err != nil { - return ValueNotDecoded - } else if sr == nil { - return ValueNil - } else { - return *sr - } - case PortableTypeTimestampArray: - return ValueNotDecoded - case PortableTypeTimestampWithTimezone: - sr, err := MarshalOffsetDateTime(f.Value) - if err != nil { - return ValueNotDecoded - } else if sr == nil { - return ValueNil - } else { - return *sr - } - case PortableTypeTimestampWithTimezoneArray: - return ValueNotDecoded - default: - return ValueUnknown - } - return v -} - -// PortableFieldType corresponds to FieldDefinitionType+1 -type PortableFieldType int32 - -const ( - PortableTypeNone PortableFieldType = 0 - PortableTypePortable PortableFieldType = 1 - PortableTypeByte PortableFieldType = 2 - PortableTypeBool PortableFieldType = 3 - PortableTypeUint16 PortableFieldType = 4 - PortableTypeInt16 PortableFieldType = 5 - PortableTypeInt32 PortableFieldType = 6 - PortableTypeInt64 PortableFieldType = 7 - PortableTypeFloat32 PortableFieldType = 8 - PortableTypeFloat64 PortableFieldType = 9 - PortableTypeString PortableFieldType = 10 - PortableTypePortableArray PortableFieldType = 11 - PortableTypeByteArray PortableFieldType = 12 - PortableTypeBoolArray PortableFieldType = 13 - PortableTypeUInt16Array PortableFieldType = 14 - PortableTypeInt16Array PortableFieldType = 15 - PortableTypeInt32Array PortableFieldType = 16 - PortableTypeInt64Array PortableFieldType = 17 - PortableTypeFloat32Array PortableFieldType = 18 - PortableTypeFloat64Array PortableFieldType = 19 - PortableTypeStringArray PortableFieldType = 20 - PortableTypeDecimal PortableFieldType = 21 - PortableTypeDecimalArray PortableFieldType = 22 - PortableTypeTime PortableFieldType = 23 - PortableTypeTimeArray PortableFieldType = 24 - PortableTypeDate PortableFieldType = 25 - PortableTypeDateArray PortableFieldType = 26 - PortableTypeTimestamp PortableFieldType = 27 - PortableTypeTimestampArray PortableFieldType = 28 - PortableTypeTimestampWithTimezone PortableFieldType = 29 - PortableTypeTimestampWithTimezoneArray PortableFieldType = 30 -) - -var portableFieldTypeToString = map[PortableFieldType]string{ - PortableTypeNone: "", - PortableTypePortable: "portable", - PortableTypeByte: "byte", - PortableTypeBool: "bool", - PortableTypeUint16: "uint16", - PortableTypeInt16: "int16", - PortableTypeInt32: "int32", - PortableTypeInt64: "int64", - PortableTypeFloat32: "float32", - PortableTypeFloat64: "float64", - PortableTypeString: "string", - PortableTypePortableArray: "portablearray", - PortableTypeByteArray: "bytearray", - PortableTypeBoolArray: "boolarray", - PortableTypeUInt16Array: "uint16array", - PortableTypeInt16Array: "int16array", - PortableTypeInt32Array: "int32array", - PortableTypeInt64Array: "int64array", - PortableTypeFloat32Array: "float32array", - PortableTypeFloat64Array: "float64array", - PortableTypeStringArray: "stringarray", - PortableTypeDecimal: "decimal", - PortableTypeDecimalArray: "decimalarray", - PortableTypeTime: "time", - PortableTypeTimeArray: "timearray", - PortableTypeDate: "date", - PortableTypeDateArray: "datearray", - PortableTypeTimestamp: "timestamp", - PortableTypeTimestampArray: "timestamparray", - PortableTypeTimestampWithTimezone: "timestampwithtimezone", - PortableTypeTimestampWithTimezoneArray: "timestampwithtimezonearray", -} - -func (t PortableFieldType) MarshalText() ([]byte, error) { - s, ok := portableFieldTypeToString[t] - if !ok { - return nil, fmt.Errorf("unknown portable type: %d", t) - } - return []byte(s), nil -} - -var stringToPortableFieldType = map[string]PortableFieldType{ - "": PortableTypeNone, - "byte": PortableTypePortable, - "bool": PortableTypeBool, - "uint16": PortableTypeUint16, - "int16": PortableTypeInt16, - "int32": PortableTypeInt32, - "int64": PortableTypeInt64, - "float32": PortableTypeFloat32, - "float64": PortableTypeFloat64, - "string": PortableTypeString, - "portablearray": PortableTypePortableArray, - "bytearray": PortableTypeByteArray, - "boolarray": PortableTypeBoolArray, - "uint16array": PortableTypeUInt16Array, - "int16array": PortableTypeInt16Array, - "int32array": PortableTypeInt32Array, - "int64array": PortableTypeInt64Array, - "float32array": PortableTypeFloat32Array, - "float64array": PortableTypeFloat64Array, - "stringarray": PortableTypeStringArray, - "decimal": PortableTypeDecimal, - "decimalarray": PortableTypeDecimalArray, - "time": PortableTypeTime, - "timearray": PortableTypeTimeArray, - "date": PortableTypeDate, - "datearray": PortableTypeDateArray, - "timestamp": PortableTypeTimestamp, - "timestamparray": PortableTypeTimestampArray, - "timestampwithtimezone": PortableTypeTimestampWithTimezone, - "timestampwithtimezonearray": PortableTypeTimestampWithTimezoneArray, -} - -func (t *PortableFieldType) UnmarshalText(b []byte) error { - ft, ok := stringToPortableFieldType[strings.ToLower(string(b))] - if !ok { - ft = PortableTypePortable - } - *t = ft - return nil -} - -var portableFieldTypeToTypeID = map[PortableFieldType]int32{ - PortableTypeNone: TypeNil, - PortableTypePortable: TypePortable, - PortableTypeByte: TypeByte, - PortableTypeBool: TypeBool, - PortableTypeUint16: TypeUInt16, - PortableTypeInt16: TypeInt16, - PortableTypeInt32: TypeInt32, - PortableTypeInt64: TypeInt64, - PortableTypeFloat32: TypeFloat32, - PortableTypeFloat64: TypeFloat64, - PortableTypeString: TypeString, - PortableTypePortableArray: TypeJavaArray, - PortableTypeByteArray: TypeByteArray, - PortableTypeBoolArray: TypeBoolArray, - PortableTypeUInt16Array: TypeUInt16Array, - PortableTypeInt16Array: TypeInt16Array, - PortableTypeInt32Array: TypeInt32Array, - PortableTypeInt64Array: TypeInt64Array, - PortableTypeFloat32Array: TypeFloat32Array, - PortableTypeFloat64Array: TypeFloat64Array, - PortableTypeStringArray: TypeStringArray, - PortableTypeDecimal: TypeJavaDecimal, - PortableTypeDecimalArray: TypeJavaArray, - PortableTypeTime: TypeJavaLocalTime, - PortableTypeTimeArray: TypeJavaArray, - PortableTypeDate: TypeJavaLocalDate, - PortableTypeDateArray: TypeJavaArray, - PortableTypeTimestamp: TypeJavaLocalDateTime, - PortableTypeTimestampArray: TypeJavaArray, - PortableTypeTimestampWithTimezone: TypeJavaOffsetDateTime, - PortableTypeTimestampWithTimezoneArray: TypeJavaArray, -} - -func (t *PortableFieldType) ToTypeID() int32 { - id, ok := portableFieldTypeToTypeID[*t] - if !ok { - return TypeUnknown - } - return id - +var FieldDefinitionIDToType = map[serialization.FieldDefinitionType]int32{ + serialization.TypePortable: TypePortable, + serialization.TypeByte: TypeByte, + serialization.TypeBool: TypeBool, + serialization.TypeUint16: TypeUInt16, + serialization.TypeInt16: TypeInt16, + serialization.TypeInt32: TypeInt32, + serialization.TypeInt64: TypeInt64, + serialization.TypeFloat32: TypeFloat32, + serialization.TypeFloat64: TypeFloat64, + serialization.TypeString: TypeString, + serialization.TypePortableArray: TypeJavaArray, + serialization.TypeByteArray: TypeByteArray, + serialization.TypeBoolArray: TypeBoolArray, + serialization.TypeUInt16Array: TypeUInt16Array, + serialization.TypeInt16Array: TypeInt16Array, + serialization.TypeInt32Array: TypeInt32Array, + serialization.TypeInt64Array: TypeInt64Array, + serialization.TypeFloat32Array: TypeFloat32Array, + serialization.TypeFloat64Array: TypeFloat64Array, + serialization.TypeStringArray: TypeStringArray, + serialization.TypeDecimal: TypeJavaDecimal, + serialization.TypeDecimalArray: TypeDecimalArray, + serialization.TypeTime: TypeJavaLocalTime, + serialization.TypeTimeArray: TypeJavaLocalTimeArray, + serialization.TypeDate: TypeJavaLocalDate, + serialization.TypeDateArray: TypeJavaLocalDateArray, + serialization.TypeTimestamp: TypeJavaLocalDateTime, + serialization.TypeTimestampArray: TypeJavaLocalDateTimeArray, + serialization.TypeTimestampWithTimezone: TypeJavaOffsetDateTime, + serialization.TypeTimestampWithTimezoneArray: TypeJavaOffsetDateTimeArray, } var portableReaders = map[serialization.FieldDefinitionType]portableFieldReader{ diff --git a/internal/serialization/portable_serialization_it_test.go b/internal/serialization/portable_serialization_it_test.go new file mode 100644 index 00000000..7575d301 --- /dev/null +++ b/internal/serialization/portable_serialization_it_test.go @@ -0,0 +1,217 @@ +package serialization_test + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + "github.com/hazelcast/hazelcast-go-client" + "github.com/hazelcast/hazelcast-go-client/serialization" + "github.com/hazelcast/hazelcast-go-client/types" + + "github.com/hazelcast/hazelcast-commandline-client/internal/check" + "github.com/hazelcast/hazelcast-commandline-client/internal/it" +) + +func TestPortableSerialization(t *testing.T) { + testCases := []struct { + name string + f func(t *testing.T) + }{ + {name: "PortableOthers", f: portableOthersTest}, + {name: "PortablePrimitives", f: portablePrimitivesTest}, + } + for _, tc := range testCases { + t.Run(tc.name, tc.f) + } +} + +func portablePrimitivesTest(t *testing.T) { + portableTester(t, func(tcx it.TestContext) { + value := &primitives2{ + valueByte: 8, + valueBool: true, + } + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "value", value)) + testCases := []struct { + format string + target string + }{ + { + format: "delimited", + target: "valueBool:true; valueByte:8\n", + }, + { + format: "json", + target: `{"this":{"valueBool":true,"valueByte":8}}` + "\n", + }, + { + format: "csv", + target: "this\n" + "valueBool:true; valueByte:8\n", + }, + { + format: "table", + target: "testdata/portable_primitives_table.txt", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "value", "--quite", "-f", tc.format)) + if tc.format == "table" { + tcx.AssertStdoutDollarWithPath(tc.target) + } else { + tcx.AssertStdoutEquals(tc.target) + } + }) + }) + } + }) + }) +} + +func portableOthersTest(t *testing.T) { + portableTester(t, func(tcx it.TestContext) { + dtz := time.Date(2023, 4, 5, 12, 33, 45, 46, time.UTC) + dc := types.NewDecimal(big.NewInt(1234), 56) + value := &others2{ + valueString: "foobar", + offsetDateTimeNotNull: (*types.OffsetDateTime)(&dtz), + decimalNotNull: &dc, + } + ctx := context.Background() + it.WithMap(tcx, func(m *hazelcast.Map) { + check.Must(m.Set(ctx, "value", value)) + testCases := []struct { + format string + target string + }{ + { + format: "delimited", + target: "decimalNotNull:1.234E-53; decimalNull:-; offsetDateTimeNotNull:2023-04-05T12:33:45Z; offsetDateTimeNull:-; valueString:foobar\n", + }, + { + format: "json", + target: `{"this":{"decimalNotNull":"1.234E-53","decimalNull":null,"offsetDateTimeNotNull":"2023-04-05T12:33:45Z","offsetDateTimeNull":null,"valueString":"foobar"}}` + "\n", + }, + { + format: "csv", + target: "this\n" + `decimalNotNull:1.234E-53; decimalNull:-; offsetDateTimeNotNull:2023-04-05T12:33:45Z; offsetDateTimeNull:-; valueString:foobar` + "\n", + }, + { + format: "table", + target: "testdata/portable_others_table.txt", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.format, func(t *testing.T) { + tcx.T = t + tcx.WithReset(func() { + check.Must(tcx.CLC().Execute("map", "-n", m.Name(), "get", "value", "--quite", "-f", tc.format)) + if tc.format == "table" { + tcx.AssertStdoutDollarWithPath(tc.target) + } else { + tcx.AssertStdoutEquals(tc.target) + } + }) + }) + } + }) + }) +} + +const ( + factoryID = 1000 + othersClassID = 10 + primitivesClassID = 11 +) + +// duplicated the others to be able to serialize in portable + +type others2 struct { + valueString string + offsetDateTimeNull *types.OffsetDateTime + offsetDateTimeNotNull *types.OffsetDateTime + decimalNull *types.Decimal + decimalNotNull *types.Decimal +} + +func (o *others2) FactoryID() int32 { + return factoryID +} + +func (o *others2) ClassID() int32 { + return othersClassID +} + +func (o *others2) WritePortable(w serialization.PortableWriter) { + w.WriteString("valueString", o.valueString) + w.WriteTimestampWithTimezone("offsetDateTimeNull", o.offsetDateTimeNull) + w.WriteTimestampWithTimezone("offsetDateTimeNotNull", o.offsetDateTimeNotNull) + w.WriteDecimal("decimalNull", o.decimalNull) + w.WriteDecimal("decimalNotNull", o.decimalNotNull) +} + +func (o *others2) ReadPortable(r serialization.PortableReader) { + o.valueString = r.ReadString("valueString") + o.offsetDateTimeNull = r.ReadTimestampWithTimezone("offsetDateTimeNull") + o.offsetDateTimeNotNull = r.ReadTimestampWithTimezone("offsetDateTimeNotNull") + o.decimalNull = r.ReadDecimal("decimalNull") + o.decimalNotNull = r.ReadDecimal("decimalNotNull") +} + +type primitives2 struct { + valueByte byte + valueBool bool +} + +func (p *primitives2) FactoryID() int32 { + return factoryID +} + +func (p *primitives2) ClassID() int32 { + return primitivesClassID +} + +func (p *primitives2) WritePortable(w serialization.PortableWriter) { + w.WriteByte("valueByte", p.valueByte) + w.WriteBool("valueBool", p.valueBool) +} + +func (p *primitives2) ReadPortable(r serialization.PortableReader) { + p.valueByte = r.ReadByte("valueByte") + p.valueBool = r.ReadBool("valueBool") +} + +type portableFactory struct{} + +func (p portableFactory) Create(cid int32) serialization.Portable { + switch cid { + case othersClassID: + return &others2{} + case primitivesClassID: + return &primitives2{} + } + panic(fmt.Sprintf("unknown cid: %d", cid)) +} + +func (p portableFactory) FactoryID() int32 { + return factoryID +} + +func portableTester(t *testing.T, f func(tcx it.TestContext)) { + tcx := it.TestContext{ + T: t, + ConfigCallback: func(tcx it.TestContext) { + tcx.ClientConfig.Serialization.SetPortableFactories(&portableFactory{}) + }, + } + tcx.Tester(f) +} diff --git a/internal/serialization/serialization_consts.go b/internal/serialization/serialization_consts.go index 297b5dbd..277c26f7 100644 --- a/internal/serialization/serialization_consts.go +++ b/internal/serialization/serialization_consts.go @@ -16,7 +16,9 @@ package serialization -import "fmt" +import ( + "fmt" +) const ( TypeNil = 0 @@ -91,6 +93,18 @@ const ( TypeHibernate5TypeHibernateNaturalIDKey = -206 TypeJetSerializerFirst = -300 TypeJetSerializerLast = -399 + + // extra types + + TypeDecimalArray = -500 + TypeJavaLocalTimeArray = -501 + TypeJavaLocalDateArray = -502 + TypeJavaLocalDateTimeArray = -503 + TypeJavaOffsetDateTimeArray = -504 + TypeCompactArray = -505 + TypeInt8 = -506 + TypeInt8Array = -507 + // TypeUnknown is the type of values with unknown types // does not exist in the reference implementation TypeUnknown = -2022 @@ -100,7 +114,7 @@ const ( TypeNotDecoded = -2024 ) -var typeToString = map[int32]string{ +var typeToLabel = map[int32]string{ TypeNil: "NIL", TypePortable: "PORTABLE", TypeDataSerializable: "DATA_SERIALIZABLE", @@ -127,8 +141,8 @@ var typeToString = map[int32]string{ TypeSimpleImmutableEntry: "SIMPLE_IMMUTABLE_ENTRY", TypeJavaClass: "JAVA_CLASS", TypeJavaDate: "JAVA_DATE", - TypeJavaBigInteger: "JAVA_BIG_INTEGER", - TypeJavaDecimal: "JAVA_DECIMAL", + TypeJavaBigInteger: "BIG_INTEGER", + TypeJavaDecimal: "DECIMAL", TypeJavaArray: "JAVA_ARRAY", TypeJavaArrayList: "JAVA_ARRAY_LIST", TypeJavaLinkedList: "JAVA_LINKED_LIST", @@ -152,16 +166,16 @@ var typeToString = map[int32]string{ TypeJavaDefaultTypeLinkedTransferQueue: "JAVA_LINKED_TRANSFER_QUEUE", TypeJavaDefaultTypePriorityQueue: "JAVA_PRIORITY_QUEUE", TypeJavaDefaultTypeOptional: "JAVA_OPTIONAL", - TypeJavaLocalDate: "JAVA_LOCALDATE", - TypeJavaLocalTime: "JAVA_LOCALTIME", - TypeJavaLocalDateTime: "JAVA_LOCALDATETIME", - TypeJavaOffsetDateTime: "JAVA_OFFSETDATETIME", + TypeJavaLocalDate: "LOCALDATE", + TypeJavaLocalTime: "LOCALTIME", + TypeJavaLocalDateTime: "LOCALDATETIME", + TypeJavaOffsetDateTime: "OFFSETDATETIME", TypeCompact: "COMPACT", TypeCompactWithSchema: "COMPACT_WITH_SCHEMA", TypeJavaDefaultTypeSerializable: "JAVA_SERIALIZABLE", TypeJavaDefaultTypeExternalizable: "JAVA_EXTERNALIZABLE", - TypeCsharpCLRSerializationType: "CSHARP_CLR_SERIALIZATION", - TypePythonPickleSerializationType: "PYTHON_PICKLE_SERIALIZATION", + TypeCsharpCLRSerializationType: "NET_CLR", + TypePythonPickleSerializationType: "PYTHON_PICKLE", TypeJSONSerialization: "JSON", TypeGobSerialization: "GO_GOB_SERIALIZATION", TypeHibernate3TypeHibernateCacheKey: "HIBERNATE3_CACHE_KEY", @@ -173,10 +187,19 @@ var typeToString = map[int32]string{ TypeHibernate5TypeHibernateNaturalIDKey: "HIBERNATE5_NATURAL_ID_KEY", TypeJetSerializerFirst: "JET_SERIALIZER_FIRST", TypeJetSerializerLast: "JET_SERIALIZER_LAST", + TypeDecimalArray: "DECIMAL_ARRAY", + TypeJavaLocalTimeArray: "LOCALTIME_ARRAY", + TypeJavaLocalDateArray: "LOCALDATE_ARRAY", + TypeJavaLocalDateTimeArray: "LOCALDATETIME_ARRAY", + TypeJavaOffsetDateTimeArray: "OFFSETDATETIME_ARRAY", + TypeCompactArray: "COMPACT_ARRAY", + TypeUnknown: "UNKNOWN", + TypeSkip: ValueSkip, + TypeNotDecoded: ValueNotDecoded, } -func TypeToString(t int32) string { - s, ok := typeToString[t] +func TypeToLabel(t int32) string { + s, ok := typeToLabel[t] if !ok { return fmt.Sprintf("UNKNOWN_TYPE:%d", t) } diff --git a/internal/serialization/stringer_test.go b/internal/serialization/stringer_test.go new file mode 100644 index 00000000..a0f08721 --- /dev/null +++ b/internal/serialization/stringer_test.go @@ -0,0 +1,97 @@ +package serialization + +import ( + "testing" + "time" + + "github.com/hazelcast/hazelcast-go-client/types" + "github.com/stretchr/testify/require" +) + +func TestTimeStringer(t *testing.T) { + var nullTm *time.Time + tm := time.Date(2023, 1, 2, 3, 4, 5, 6, time.UTC) + testCases := []struct { + name string + in any + target string + }{ + { + name: "null time.Time", + in: nullTm, + target: ValueNil, + }, + { + name: "time.Time", + in: tm, + target: "2023-01-02T03:04:05Z", + }, + { + name: "null *types.LocalTime", + in: (*types.LocalTime)(nullTm), + target: ValueNil, + }, + { + name: "*types.LocalTime", + in: (*types.LocalTime)(&tm), + target: "03:04:05", + }, + { + name: "types.LocalTime", + in: (types.LocalTime)(tm), + target: "03:04:05", + }, + { + name: "null *types.LocalDate", + in: (*types.LocalDate)(nullTm), + target: ValueNil, + }, + { + name: "*types.LocalDate", + in: (*types.LocalDate)(&tm), + target: "2023-01-02", + }, + { + name: "types.LocalDate", + in: (types.LocalDate)(tm), + target: "2023-01-02", + }, + { + name: "null *types.LocalDateTime", + in: (*types.LocalDateTime)(nullTm), + target: ValueNil, + }, + { + name: "*types.LocalDateTime", + in: (*types.LocalDateTime)(&tm), + target: "2023-01-02 03:04:05", + }, + { + name: "types.LocalDateTime", + in: (types.LocalDateTime)(tm), + target: "2023-01-02 03:04:05", + }, + { + name: "null *types.OffsetDateTime", + in: (*types.OffsetDateTime)(nullTm), + target: ValueNil, + }, + { + name: "*types.OffsetDateTime", + in: (*types.OffsetDateTime)(&tm), + target: "2023-01-02T03:04:05Z", + }, + { + name: "types.OffsetDateTime", + in: (types.OffsetDateTime)(tm), + target: "2023-01-02T03:04:05Z", + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + v := timeStringer(tc.in) + require.Equal(t, tc.target, v) + }) + } +} diff --git a/internal/serialization/testdata/builtin_byte.txt b/internal/serialization/testdata/builtin_byte.txt new file mode 100644 index 00000000..985bb966 --- /dev/null +++ b/internal/serialization/testdata/builtin_byte.txt @@ -0,0 +1,5 @@ +$------$ +$ this $ +$------$ +$ 8 $ +$------$ \ No newline at end of file diff --git a/internal/serialization/testdata/builtin_serialization.json.txt b/internal/serialization/testdata/builtin_serialization.json.txt new file mode 100644 index 00000000..6a8de5f8 --- /dev/null +++ b/internal/serialization/testdata/builtin_serialization.json.txt @@ -0,0 +1,5 @@ +$---------------------------$ +$ FieldA | FieldB $ +$---------------------------$ +$ json-str-1 | 22 $ +$---------------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/builtin_slice.txt b/internal/serialization/testdata/builtin_slice.txt new file mode 100644 index 00000000..bd4cd965 --- /dev/null +++ b/internal/serialization/testdata/builtin_slice.txt @@ -0,0 +1,5 @@ +$-----------------------$ +$ this $ +$-----------------------$ +$ [100, foo, 200, true] $ +$-----------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/builtin_string.txt b/internal/serialization/testdata/builtin_string.txt new file mode 100644 index 00000000..16a2ba47 --- /dev/null +++ b/internal/serialization/testdata/builtin_string.txt @@ -0,0 +1,5 @@ +$-------------$ +$ this $ +$-------------$ +$ test-string $ +$-------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/builtin_types.decimal.txt b/internal/serialization/testdata/builtin_types.decimal.txt new file mode 100644 index 00000000..49958b0d --- /dev/null +++ b/internal/serialization/testdata/builtin_types.decimal.txt @@ -0,0 +1,4 @@ +$ this $ +$---------$ +$ 1.00E-8 $ +$---------$ \ No newline at end of file diff --git a/internal/serialization/testdata/builtin_types.offsetdatetime.txt b/internal/serialization/testdata/builtin_types.offsetdatetime.txt new file mode 100644 index 00000000..9f7d7dd3 --- /dev/null +++ b/internal/serialization/testdata/builtin_types.offsetdatetime.txt @@ -0,0 +1,5 @@ +$----------------------$ +$ this $ +$----------------------$ +$ 2023-02-03T04:05:06Z $ +$----------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/compact_others_table.txt b/internal/serialization/testdata/compact_others_table.txt new file mode 100644 index 00000000..e6462e9e --- /dev/null +++ b/internal/serialization/testdata/compact_others_table.txt @@ -0,0 +1,5 @@ +$----------------------------------------------------------------------------------------------------------------------------------------------$ +$ decimalNotNull | decimalNull | nullStringNotNull | nullStringNull | offsetDateTimeNotNull | offsetDateTimeNull$ +$----------------------------------------------------------------------------------------------------------------------------------------------$ +$ 1.234E-53 | - | foobar | - | 2023-04-05T12:33:45Z | - $ +$----------------------------------------------------------------------------------------------------------------------------------------------$ diff --git a/internal/serialization/testdata/compact_primitives_table.txt b/internal/serialization/testdata/compact_primitives_table.txt new file mode 100644 index 00000000..aad2a3bf --- /dev/null +++ b/internal/serialization/testdata/compact_primitives_table.txt @@ -0,0 +1,5 @@ +$-----------------------------------------------------------------------------------------------------------------------$ +$ nullBoolNotNull | nullBoolNull | nullInt8NotNull | nullInt8Null | valueBool | valueInt8 $ +$-----------------------------------------------------------------------------------------------------------------------$ +$ false | - | 8 | - | true | 8 $ +$-----------------------------------------------------------------------------------------------------------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/portable_others_table.txt b/internal/serialization/testdata/portable_others_table.txt new file mode 100644 index 00000000..24f342c9 --- /dev/null +++ b/internal/serialization/testdata/portable_others_table.txt @@ -0,0 +1,5 @@ +$------------------------------------------------------------------------------------------------------------------$ +$ decimalNotNull | decimalNull | offsetDateTimeNotNull | offsetDateTimeNull | valueString $ +$------------------------------------------------------------------------------------------------------------------$ +$ 1.234E-53 | - | 2023-04-05T12:33:45Z | - | foobar $ +$------------------------------------------------------------------------------------------------------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/portable_primitives_table.txt b/internal/serialization/testdata/portable_primitives_table.txt new file mode 100644 index 00000000..af3d5261 --- /dev/null +++ b/internal/serialization/testdata/portable_primitives_table.txt @@ -0,0 +1,5 @@ +$---------------------------------$ +$ valueBool | valueByte $ +$---------------------------------$ +$ true | 8 $ +$---------------------------------$ \ No newline at end of file diff --git a/internal/serialization/testdata/primitive_arrays_table.txt b/internal/serialization/testdata/primitive_arrays_table.txt new file mode 100644 index 00000000..e95a7046 --- /dev/null +++ b/internal/serialization/testdata/primitive_arrays_table.txt @@ -0,0 +1,5 @@ +$-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------$ +$ emptyBoolArray | emptyFloat32Array | emptyFloat64Array | emptyInt16Array | emptyInt32Array | emptyInt64Array | emptyInt8Array | emptyNullableBoolArray | emptyNullableInt8Array | fullBoolArray | fullFloat32Array | fullFloat64Array | fullInt16Array | fullInt32Array | fullInt64Array | fullInt8Array | fullNullableBoolArray | fullNullableInt8Array $ +$-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------$ +$ [] | [] | [] | [] | [] | [] | [] | [] | [] | [true, false] | [] | [] | [-32768, 0, 32767] | [-2147483648, 0, 2147483647] | [-9223372036854775808, 0, -9223372036854775808] | [-128, 0, 127] | [true, -] | [-, 8] $ +$-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------$ \ No newline at end of file diff --git a/internal/serialization/types.go b/internal/serialization/types.go index 09c1788f..ccb4c23f 100644 --- a/internal/serialization/types.go +++ b/internal/serialization/types.go @@ -1,3 +1,232 @@ package serialization +import ( + "fmt" + "reflect" + "strings" + "time" + + "github.com/hazelcast/hazelcast-go-client/types" + + "github.com/hazelcast/hazelcast-commandline-client/internal/check" +) + type NondecodedType string + +type JSONValuer interface { + JSONValue() (any, error) +} + +type Texter interface { + Text() string +} + +func jsonValueToTypeID(v any) int32 { + switch v.(type) { + case []any: + return TypeJavaArray + case string: + return TypeString + case float64: + return TypeFloat64 + case bool: + return TypeBool + case nil: + return TypeNil + } + return TypeUnknown +} + +type Stringer func(any) string + +func staticStringer(v string) Stringer { + return func(a any) string { + return v + } +} + +func sprintStringer(v any) string { + if check.IsNil(v) { + return ValueNil + } + return fmt.Sprint(v) +} + +func ptrStringer[T any]() Stringer { + return func(v any) string { + if vv, ok := v.(T); ok { + return fmt.Sprint(vv) + } + if vv, ok := v.(*T); ok { + if vv == nil { + return ValueNil + } + return fmt.Sprint(*vv) + } + return fmt.Sprintf("?%v?", reflect.TypeOf(v)) + } +} + +func sprintNilStringer[T any](v *T) string { + if v == (*T)(nil) { + return ValueNil + } + return fmt.Sprint(*v) +} + +func arrayStringer[T any](v any) string { + vv, ok := v.([]T) + if !ok { + if vv, ok := v.([]*T); ok { + return arrayPtrStringer[T](vv) + } + return fmt.Sprintf("?%v?", reflect.TypeOf(v)) + } + var sb strings.Builder + sb.WriteString("[") + if len(vv) > 0 { + sb.WriteString(fmt.Sprint(vv[0])) + for _, x := range vv[1:] { + sb.WriteString(", ") + sb.WriteString(fmt.Sprint(x)) + } + } + sb.WriteString("]") + return sb.String() +} + +func arrayPtrStringer[T any](v any) string { + vv := v.([]*T) + var sb strings.Builder + sb.WriteString("[") + if len(vv) > 0 { + sb.WriteString(sprintNilStringer(vv[0])) + for _, x := range vv[1:] { + sb.WriteString(", ") + sb.WriteString(sprintNilStringer(x)) + } + } + sb.WriteString("]") + return sb.String() +} + +func arrayAnySerializer(v any) string { + vv, ok := v.([]any) + if !ok { + return "?array?" + } + var sb strings.Builder + sb.WriteString("[") + if len(vv) > 0 { + sb.WriteString(sprintStringer(vv[0])) + for _, x := range vv[1:] { + sb.WriteString(", ") + sb.WriteString(sprintStringer(x)) + } + } + sb.WriteString("]") + return sb.String() +} + +func javaClassStringer(v any) string { + return fmt.Sprintf("Java Class: %v", v) +} + +func timeStringer(v any) string { + switch vv := v.(type) { + case time.Time: + return vv.Format(time.RFC3339) + case *time.Time: + if vv == nil { + return ValueNil + } + return vv.Format(time.RFC3339) + case types.LocalTime: + return time.Time(vv).Format("15:04:05") + case *types.LocalTime: + if vv == nil { + return ValueNil + } + return (*time.Time)(vv).Format("15:04:05") + case types.LocalDate: + return time.Time(vv).Format("2006-01-02") + case *types.LocalDate: + if vv == nil { + return ValueNil + } + return (*time.Time)(vv).Format("2006-01-02") + case types.LocalDateTime: + return time.Time(vv).Format("2006-01-02 15:04:05") + case *types.LocalDateTime: + if vv == nil { + return ValueNil + } + return (*time.Time)(vv).Format("2006-01-02 15:04:05") + case types.OffsetDateTime: + return time.Time(vv).Format(time.RFC3339) + case *types.OffsetDateTime: + if vv == nil { + return ValueNil + } + return (*time.Time)(vv).Format(time.RFC3339) + } + return "?datetime?" +} + +func portableStringer(v any) string { + vv := v.(*GenericPortable) + return vv.Text() +} + +var ValueToText map[int32]Stringer + +func init() { + ValueToText = map[int32]Stringer{ + TypeNil: staticStringer(ValueNil), + + TypePortable: portableStringer, + // TypeDataSerializable + + TypeByte: ptrStringer[byte](), // + + TypeBool: ptrStringer[bool](), // + + TypeUInt16: ptrStringer[uint16](), + TypeInt16: ptrStringer[int16](), + TypeInt32: ptrStringer[int32](), + TypeInt64: ptrStringer[int64](), + TypeFloat32: ptrStringer[float32](), + TypeFloat64: ptrStringer[float64](), + TypeString: ptrStringer[string](), // + + TypeByteArray: arrayStringer[uint8], + TypeBoolArray: arrayStringer[bool], + TypeUInt16Array: arrayStringer[uint16], + TypeInt16Array: arrayStringer[int16], + TypeInt32Array: arrayStringer[int32], + TypeInt64Array: arrayStringer[int64], + TypeFloat32Array: arrayStringer[float32], + TypeFloat64Array: arrayStringer[float64], + TypeStringArray: arrayStringer[string], + TypeUUID: sprintStringer, + + // TypeSimpleEntry + // TypeSimpleImmutableEntry + + TypeJavaClass: javaClassStringer, + TypeJavaDate: timeStringer, + TypeJavaBigInteger: sprintStringer, + TypeJavaDecimal: ptrStringer[types.Decimal](), // + + TypeJavaArray: arrayAnySerializer, // + + TypeJavaArrayList: arrayAnySerializer, // + + TypeJavaLinkedList: arrayAnySerializer, // + + TypeJavaLocalDate: timeStringer, + TypeJavaLocalTime: timeStringer, + TypeJavaLocalDateTime: timeStringer, + TypeJavaOffsetDateTime: timeStringer, // + + + TypeJSONSerialization: sprintStringer, + + // + + TypeInt8: ptrStringer[int8](), + TypeInt8Array: arrayStringer[int8], + } +}